Android - 为什么ConnectivityManager.isActiveNetworkMetered总是返回true(即使在Wifi上)

sel*_*bie 8 android wifi android-wifi

在我的Android音乐流应用程序中,下面的代码路径有助于根据ConnectivityManager类实例返回的值在高比特率流与低比特率之间进行决策.

我试图理解为什么ConnectivityManager.isActiveNetworkMetered()将返回"true",这显然我在本地Wifi网络上.采用以下Android/Java代码:

boolean isMetered = false;
boolean isWifi = false;
boolean isEthernet = false;
boolean isRoaming = false;
boolean isConnected = false;

NetworkInfo netinfo = connManager.getActiveNetworkInfo();
if (netinfo != null)
{
    isWifi = (netinfo.getType() == ConnectivityManager.TYPE_WIFI);
    isEthernet = (netinfo.getType() == ConnectivityManager.TYPE_ETHERNET);
    isRoaming = netinfo.isRoaming();
    isConnected = netinfo.isConnected();

    Log.d(TAG, "active network type = " + netinfo.getTypeName());
    Log.d(TAG, "active network subtype = " + netinfo.getSubtypeName());


    Log.d(TAG, "isConnected = " + isConnected);
    Log.d(TAG, "isWifi = " + isWifi);
    Log.d(TAG, "isEthernet = " + isEthernet);
    Log.d(TAG, "isRoaming = " + isRoaming);
}

// isActiveNetworkMetered was introduced in API 16 (Jelly Bean)
if (android.os.Build.VERSION.SDK_INT >= 16)
{
    isMetered = connManager.isActiveNetworkMetered();
    Log.d(TAG, "isMetered = " + isMetered);
}
Run Code Online (Sandbox Code Playgroud)

当我关闭Wifi并且我唯一的连接是AT&T的LTE网络时,它将以下内容打印到日志控制台:

active network type = mobile
active network subtype = LTE
isConnected = true
isWifi = false
isEthernet = false
isRoaming = false
isMetered = true
Run Code Online (Sandbox Code Playgroud)

以上所有内容都符合预期 - 我在一个计量的移动运营商网络上.

现在切换到我家的Wifi,同样的代码块打印出来:

active network type = WIFI
active network subtype = 
isConnected = true
isWifi = true
isEthernet = false
isRoaming = false
isMetered = true
Run Code Online (Sandbox Code Playgroud)

为什么isMetered结果isActiveNetworkMetered仍然显示为真?这导致以下代码路径支持较低比特率流,即使我在我的家庭wifi网络上:

if ((isWifi || isEthernet) && !isMetered)
{
    result = BITRATE_HIGH_KBIT_SEC;
}
else
{
    result = BITRATE_LOW_KBIT_SEC;
}
Run Code Online (Sandbox Code Playgroud)

isActiveNetworkMetered()如何决定网络是否计量?这是WIFI协商的一部分还是SSID广播中的一部分?Android上的网络设置?我在Android Kitkat设备上找不到任何设置,让我切换或发现计量网络的设置.

上周我的ISP(Frontier)给我发了一个新的Wifi路由器.可能相关?对于这样的设置,我没有在路由器的固件页面上看到任何内容.

我将在今天晚些时候开始工作,看看它在另一个网络上的表现如何.在任何情况下,我都可能编辑上面的代码以跳过计量检查,除非我能在某处证明它只是一个不正确的设置.

更新 - 总是返回计量网络的问题在我的HTC One M8手机(运行Kitkat)上重现,但在2012年我的Nexus 7上没有(也升级到Android 4.4 Kitkat).

问题已解决 - 事实证明我的Wifi被我的手机标记为"移动热点".有关如何查找和切换此标志的更多详细信息,请参见此处.

mat*_*ash 15

我也测试了这个,但我观察到了"预期的"结果:WiFi为虚假,3G为真.这是在Android 4.4.2的Nexus 4中.

奇怪的是,支持库中的ConnectivityManagerCompat类确实为WiFi返回false.

final int type = info.getType();
switch (type) {
    case TYPE_MOBILE:
        return true;
    case TYPE_WIFI:
        return false;
    default:
        // err on side of caution
        return true;
}
Run Code Online (Sandbox Code Playgroud)

编辑 - 发现它(我认为)

NetworkPolicyManagerService似乎是最终产生此方法结果的类.根据它,确实可以计量WiFi连接.它包含一个BroadcastReceiver,它"监听wifi状态变化以捕获计量提示"(第567行).这些信息来自NetworkInfo.getMeteredHint(),经过仔细检查,包含这个有趣的评论:

/**
 * Flag indicating that AP has hinted that upstream connection is metered,
 * and sensitive to heavy data transfers.
 */
private boolean mMeteredHint;
Run Code Online (Sandbox Code Playgroud)

此标志已加载 DhcpResults.hasMeteredHint()

/**
 * Test if this DHCP lease includes vendor hint that network link is
 * metered, and sensitive to heavy data transfers.
 */
public boolean hasMeteredHint() {
    if (vendorInfo != null) {
        return vendorInfo.contains("ANDROID_METERED");
    } else {
        return false;
    }
}
Run Code Online (Sandbox Code Playgroud)

实际上,AP似乎可以通过使用该标志通知其WiFi客户端计量基础互联网连接.

编辑#2相关信息,来自http://www.lorier.net/docs/android-metered

当Android手机正在使用另一个Android手机的热点时,它知道它在"计量连接"上,因此禁用了昂贵的同步选项(例如:照片同步).怎么知道这个?android热点使用值ANDROID_METERED发送DHCP选项43(供应商特定选项).如果客户端在选项43值中的任何位置看到ANDROID_METERED,则打开"昂贵的数据连接"选项.

看起来这个标志被添加到"玩好"与另一个Android设备提供的热点.

编辑#3 引入此功能的提交:

连接针对Wi-Fi网络的计量DHCP提示.

当DHCP租约包含指示远程Wi-Fi网络计量的供应商信息时,建议NetworkPolicy.用户仍然可以手动更改"设置"中的计量标记.

您可以通过以下方式访问此设置:

设置 - >数据使用 - >(溢出菜单) - >移动热点