You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Android 12绑定指定网卡后无法发现BACnet设备问题求助

Android 12双网络接口下Ethernet绑定后无法发现BACnet设备问题

问题背景

我正在开发一款Android应用,计划通过以太网(Ethernet)发现本地网络中的BACnet设备,同时使用Wi-Fi实现通用互联网连接,设备会同时连接Ethernet和Wi-Fi接口。

核心问题:
当应用进程绑定到指定网络接口(Ethernet或Wi-Fi)时,日志显示绑定成功,但在Ethernet和Wi-Fi同时活跃的情况下,绑定后无法通过Ethernet发现BACnet设备。该问题仅出现在Android 12(API level 31)上,相同代码在Android 7等旧版本中可正常使用任一接口发现设备。

应用行为

  • 仅单个接口(Ethernet或Wi-Fi)活跃时,应用可成功发现对应网络中的设备。
  • 双接口同时活跃时,调用bindProcessToNetwork()绑定到Ethernet接口,日志显示进程绑定成功,但无法获取BACnet设备的对象(仅能发现设备存在)。

观察到的行为

  • 应用日志明确显示进程已绑定到Ethernet(TRANSPORT_ETHERNET),但无法正常发现BACnet设备。
  • 禁用Wi-Fi后,可成功发现Ethernet网络中的BACnet设备。
  • 相同代码在Android 7上运行正常,双接口活跃时也可使用任一接口完成设备发现。

环境

  • Android版本:12(API level 31)

应用需求

  • Wi-Fi用于通用互联网访问。
  • Ethernet用于本地网络的BACnet设备发现。

已执行操作

  • 使用bindProcessToNetwork()实现网络绑定逻辑。
  • 绑定到Ethernet后通过DatagramSocket进行BACnet设备发现。
  • 已配置必要的网络权限:ACCESS_NETWORK_STATE、CHANGE_NETWORK_STATE。
  • 验证单接口活跃时设备发现功能正常。
  • 在Android 7上测试代码,功能符合预期。

原始网络绑定代码

private static void bindToBestNetwork() {
    Network[] networks = connectivityManager.getAllNetworks();
    Network ethernetNetwork = null;
    Network wifiNetwork = null;

    // 检查可用网络并优先选择Ethernet
    for (Network network : networks) {
        NetworkCapabilities capabilities = connectivityManager.getNetworkCapabilities(network);
        if (capabilities != null) {
            if (capabilities.hasTransport(NetworkCapabilities.TRANSPORT_ETHERNET)) {
                ethernetNetwork = network;
            } else if (capabilities.hasTransport(NetworkCapabilities.TRANSPORT_WIFI)) {
                wifiNetwork = network;
            }
        }
    }

    // 优先选择Ethernet,若不可用则回退到Wi-Fi
    if (ethernetNetwork != null) {
        WifiManager wifiManager = (WifiManager) context.getSystemService(Context.WIFI_SERVICE);
        wifiManager.setWifiEnabled(false);
        ConnectivityManager.setProcessDefaultNetwork(ethernetNetwork);
        boolean success = connectivityManager.bindProcessToNetwork(ethernetNetwork);
        wifiManager.disconnect();
        WifiManager.MulticastLock multicastLock = wifiManager.createMulticastLock("bacnetLock");
        multicastLock.setReferenceCounted(true);
        multicastLock.acquire();

        try {
            Log.d(TAG,"Binding socket to Ethernet network");
            DatagramSocket socket = new DatagramSocket();
            ethernetNetwork.bindSocket(socket);
        } catch (Exception e) {
            Log.d(TAG,"Exception while binding socket to Ethernet network");
            e.printStackTrace();
        }
        if (success) {
             Log.d(TAG, "Ethernet network successfully bound");
        } else {
             Log.d(TAG, "Ethernet network binding failed");
        }
    } else if (wifiNetwork != null) {
        boolean success = connectivityManager.bindProcessToNetwork(wifiNetwork);
        if (success) {
            Log.d(TAG, "Wi-Fi network successfully bound");
        } else {
            Log.d(TAG, "Wi-Fi network binding failed");
        }
    } else {
        connectivityManager.bindProcessToNetwork(null);
    }
}

// 注册Ethernet网络回调
private static void registerEthernetListener() {
    Log.d(TAG, "Registering Ethernet listener");
    NetworkRequest ethernetRequest = new NetworkRequest.Builder()
            .addTransportType(NetworkCapabilities.TRANSPORT_ETHERNET)
            .removeCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
            .build();

    ethernetCallback = new ConnectivityManager.NetworkCallback() {
        @Override
        public void onAvailable(Network network) {
            Log.d(TAG, "Ethernet network Available");
            bindToBestNetwork();
        }

        @Override
        public void onLost(Network network) {
            Log.d(TAG, "Ethernet network lost");
            bindToBestNetwork();
        }
    };

    connectivityManager.requestNetwork(ethernetRequest,ethernetCallback);
}

// 注册Wi-Fi网络回调
private static void registerWifiListener() {
    Log.d(TAG, "Registering Wi-Fi listener");
    NetworkRequest wifiRequest = new NetworkRequest.Builder()
            .removeCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
            .build();

    wifiCallback = new ConnectivityManager.NetworkCallback() {
        @Override
        public void onAvailable(Network network) {
            Log.d(TAG, "Wi-Fi network Available");
            bindToBestNetwork();
        }

        @Override
        public void onLost(Network network) {
            Log.d(TAG, "Wi-Fi network lost");
            bindToBestNetwork();
        }
    };

    connectivityManager.requestNetwork(wifiRequest, wifiCallback);
}

问题分析与解决方案

Android 12(API 31)对多网络场景的路由策略、进程绑定逻辑做了核心调整,原始代码的问题点包括:

  1. 进程绑定优先级被系统覆盖:Android 12会优先将带NET_CAPABILITY_INTERNET的网络设为默认路由,即使绑定了Ethernet,底层socket仍可能走Wi-Fi。
  2. MulticastLock作用范围错误:绑定Ethernet时操作Wi-Fi的MulticastLock,导致组播权限仅作用于Wi-Fi接口。
  3. NetworkRequest配置不合理:移除了Wi-Fi的互联网能力,同时未明确Ethernet的本地网络能力。

修正后的代码调整

1. 修正网络回调的请求配置

// 注册Ethernet监听器
private static void registerEthernetListener() {
    Log.d(TAG, "注册Ethernet监听器");
    NetworkRequest ethernetRequest = new NetworkRequest.Builder()
            .addTransportType(NetworkCapabilities.TRANSPORT_ETHERNET)
            .addCapability(NetworkCapabilities.NET_CAPABILITY_LOCAL) // 明确本地网络能力
            .removeCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
            .build();

    ethernetCallback = new ConnectivityManager.NetworkCallback() {
        @Override
        public void onAvailable(Network network) {
            Log.d(TAG, "Ethernet网络可用");
            bindToEthernetNetwork(network); // 单独绑定Ethernet,避免逻辑混淆
        }

        @Override
        public void onLost(Network network) {
            Log.d(TAG, "Ethernet网络断开");
            bindToWifiNetwork(); // 切换回Wi-Fi用于互联网访问
        }
    };

    connectivityManager.requestNetwork(ethernetRequest, ethernetCallback);
}

// 注册Wi-Fi监听器
private static void registerWifiListener() {
    Log.d(TAG, "注册Wi-Fi监听器");
    NetworkRequest wifiRequest = new NetworkRequest.Builder()
            .addTransportType(NetworkCapabilities.TRANSPORT_WIFI)
            .addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET) // 保留互联网能力
            .build();

    wifiCallback = new ConnectivityManager.NetworkCallback() {
        @Override
        public void onAvailable(Network network) {
            Log.d(TAG, "Wi-Fi网络可用");
            bindToWifiNetwork(network);
        }

        @Override
        public void onLost(Network network) {
            Log.d(TAG, "Wi-Fi网络断开");
            bindToBestNetwork(); // 回退到Ethernet或解绑
        }
    };

    connectivityManager.requestNetwork(wifiRequest, wifiCallback);
}

2. 拆分网络绑定逻辑,精准控制接口

// 绑定Ethernet网络用于BACnet发现
private static void bindToEthernetNetwork(Network ethernetNetwork) {
    if (ethernetNetwork == null) return;

    // 无需禁用Wi-Fi,通过网络隔离实现双接口并行
    boolean success = connectivityManager.bindProcessToNetwork(ethernetNetwork);

    try {
        Log.d(TAG, "绑定Socket到Ethernet网络");
        // 创建Socket并绑定到Ethernet的本地IP(提升可靠性)
        DatagramSocket socket = new DatagramSocket(null);
        socket.setReuseAddress(true);
        InetAddress ethernetAddr = getEthernetLocalAddress(ethernetNetwork);
        if (ethernetAddr != null) {
            socket.bind(new InetSocketAddress(ethernetAddr, 0));
        }
        ethernetNetwork.bindSocket(socket);
        // 保存Socket用于后续BACnet发现,避免重复创建
        bacnetDiscoverySocket = socket;
    } catch (Exception e) {
        Log.d(TAG, "绑定Socket到Ethernet网络时出错");
        e.printStackTrace();
    }

    if (success) {
        Log.d(TAG, "Ethernet网络绑定成功");
    } else {
        Log.d(TAG, "Ethernet网络绑定失败");
    }
}

// 绑定Wi-Fi网络用于互联网访问
private static void bindToWifiNetwork(Network wifiNetwork) {
    if (wifiNetwork == null) {
        connectivityManager.bindProcessToNetwork(null);
        return;
    }
    boolean success = connectivityManager.bindProcessToNetwork(wifiNetwork);
    if (success) {
        Log.d(TAG, "Wi-Fi网络绑定成功");
    } else {
        Log.d(TAG, "Wi-Fi网络绑定失败");
    }
}

// 获取Ethernet接口的本地IP地址
private static InetAddress getEthernetLocalAddress(Network network) {
    LinkProperties linkProps = connectivityManager.getLinkProperties(network);
    if (linkProps != null) {
        for (LinkAddress addr : linkProps.getLinkAddresses()) {
            InetAddress inetAddr = addr.getAddress();
            if (inetAddr.isSiteLocalAddress() && !inetAddr.isLoopbackAddress()) {
                return inetAddr;
            }
        }
    }
    return null;
}

3. 补充Android 12+所需权限

在AndroidManifest.xml中添加:

<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<uses-permission android:name="android.permission.CHANGE_WIFI_STATE" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.CHANGE_NETWORK_STATE" />

4. 极端场景下的路由强制策略

若上述调整仍无效,可直接使用Network对象专属连接,绕过进程绑定的路由干扰:

// 针对BACnet发现请求,使用Ethernet专属连接
// 示例:根据实际BACnet协议调整连接方式
DatagramSocket socket = new DatagramSocket();
ethernetNetwork.bindSocket(socket);
// 后续通过该Socket发送BACnet发现报文

内容的提问来源于stack exchange,提问作者Sathish kumar

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.14 06:17:33