Does webRTC support communication over android's USB tethering mode?

266 views
Skip to first unread message

李闯

unread,
Apr 11, 2024, 4:26:20 AMApr 11
to discuss-webrtc
I have such a requirement:
1. Real-time voice communication is required between PC (windows 10) and VR device (pico/android)
2. PC and VR are only connected through USB and do not support any other connections including wifi, 4/5G, BT, NFC, etc.
3. PC and VR are in a LAN through USB network sharing mode, so stun/turn server is not required.

Test:
I found an open source project on github to test and verify this requirement.
Project address: https://github.com/Jhuster/RTCStartupDemo

I used a wired connection between PC and PC, and the test results were OK.
I used the wireless connection between PC and VR headset, and the test results were OK.
I used a PC and a VR headset connectio through VR USB network sharing mode, and the test results were NOK.

Part of the error log is as follows:

part1:
(line 446): Get an unknown type for the interface rndis0
LocalSurfaceViewDuration: 4002 ms. Frames received: 110. Dropped: 0. Rendered: 110. Render fps: 27.5. Ave
RemoteSurfaceViewDuration: 4006 ms. Frames received: 0. Dropped: 0. Rendered: 0. Render fps: .0. Average

part2:
(line 377): Find network handle.
(line 203): Binding socket to network address failed; result: -3
(line 54): UDP bind failed with error 101
(line 1360): Net[rndis0:192.168.135.x/24:Unknown:id=3]: Allocation Phase=Udp
(line 186): Port[b37d33c0::1:0:local:Net[rndis0:192.168.135.x/24:Unknown:id=3]]: Port created with network cost 50
(line 377): Find network handle.

part3:
SdpObserver: onSetSuccess
onBroadcastReceived: {"candidate":"candidate:3149568382 1 udp 2113937151 d56d06b6-4966-461a-a2fc-bc15b207e442.local 42597
Receive Remote Candidate...
(line 1164): Asynchronously resolving ICE candidate hostname d56d06b6-4966-461a-a2fc-bc15b207e442.local
No such thread id for suspend: 36
(line 1247): Failed to resolve ICE candidate hostname d56d06b6-4966-461a-a2fc-bc15b207e442.local with error 7

I would like help with the following questions:
1. Does webRTC not support USB network sharing connection?
2. If supported, what is the reason for the above failure?

Here are some references I found:
https://stackoverflow.com/questions/64518631/can-webrtc-work-over-an-android-usb-ethernet-connection
https://groups.google.com/g/discuss-webrtc/c/SkmRIO_wcRo/m/J3zAKEchAAAJ

looking forward to your reply, thank you!

PS: Due to network reasons, I may have posted twice, please forgive me.

Roman Morgenstern

unread,
May 21, 2024, 2:28:39 PMMay 21
to discuss-webrtc

The main reason that tethering (of any kind) is not correctly supported by WebRTC on Android is that the internal network interfaces used for tethering are not included in the ICE candidate enumerations. As a result, for all those interfaces, only relay or reflexive connections are used.

A workaround can be found at the following link: https://stackoverflow.com/questions/7509924/detect-usb-tethering-on-android/

Additionally, you can use Direct Wi-Fi code to produce something like the following inside sdk/android/api/org/webrtc/NetworkMonitorAutoDetect.java:




static class TetheringDelegate extends BroadcastReceiver {

    private static final String EXTRA_ACTIVE_TETHER =
        android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.O ? "tetherArray" : "activeArray";
    private static final String EXTRA_ERRORED_TETHER = "erroredArray";
    private static final String EXTRA_AVAILABLE_TETHER = "availableArray";
    private static final String ACTION_TETHER_STATE_CHANGED = "android.net.conn.TETHER_STATE_CHANGED";

    private static final int TETHERING_NETWORK_HANDLE = 0;

    private final Context context;
    private final NetworkChangeDetector.Observer observer;
    private List<NetworkInformation> tetheringNetworkInfo;

    private static class NetworkInformationDiff {
        public List<String> added = new ArrayList<>();
        public List<NetworkInformation> unChanged = new ArrayList<>();
        public List<NetworkInformation> removed = new ArrayList<>();

        public boolean isNoChange() {
            return added.isEmpty() && removed.isEmpty();
        }

        private List<String> getIntfNames(List<NetworkInformation> netInfos) {
            List<String> intfNames = new ArrayList<>();
            for (NetworkInformation netInfo : netInfos) {
                intfNames.add(netInfo.name);
            }
            return intfNames;
        }
    }

    TetheringDelegate(NetworkChangeDetector.Observer observer, Context context) {
        this.context = context;
        this.observer = observer;
        IntentFilter intentFilter = new IntentFilter();
        intentFilter.addAction(ACTION_TETHER_STATE_CHANGED);
        context.registerReceiver(this, intentFilter);
    }

    @Override
    @SuppressLint("InlinedApi")
    public void onReceive(Context context, Intent intent) {
        if (ACTION_TETHER_STATE_CHANGED.equals(intent.getAction())) {
            List<String> tetherIfNames = intent.getStringArrayListExtra(EXTRA_ACTIVE_TETHER);
            List<String> erroredIfNames = intent.getStringArrayListExtra(EXTRA_ERRORED_TETHER);
            List<String> availableIfNames = intent.getStringArrayListExtra(EXTRA_AVAILABLE_TETHER);
            onTetheringStateChange(tetherIfNames, erroredIfNames, availableIfNames);
        }
    }

    public void release() {
        context.unregisterReceiver(this);
    }

    public List<NetworkInformation> getActiveNetworkList() {
        return tetheringNetworkInfo != null ? tetheringNetworkInfo : Collections.emptyList();
    }

    private NetworkInformationDiff computeNetworkInfoDiff(List<String> newNetIntfs) {
        Map<String, NetworkInformation> oldMap = new HashMap<>();
        NetworkInformationDiff diff = new NetworkInformationDiff();
        Set<String> newNetInfsSet = new HashSet<>(newNetIntfs);

        if (tetheringNetworkInfo != null) {
            for (NetworkInformation oldNetInfo : tetheringNetworkInfo) {
                oldMap.put(oldNetInfo.name, oldNetInfo);
            }
        }

        for (String oldNetInfoName : oldMap.keySet()) {
            newNetInfsSet.remove(oldNetInfoName);
        }

        for (String newNetInfoName : newNetInfsSet) {
            NetworkInformation unChanged = oldMap.remove(newNetInfoName);
            if (unChanged != null) {
                diff.unChanged.add(unChanged);
            }
        }

        diff.added.addAll(newNetInfsSet);
        diff.removed.addAll(oldMap.values());

        return diff;
    }

    private void onTetheringStateChange(List<String> tetherIfNames,
                                        List<String> erroredIfNames,
                                        List<String> availableIfNames) {
        NetworkInformationDiff diff = computeNetworkInfoDiff(tetherIfNames);

        if (diff.isNoChange()) {
            return;
        }

        List<NetworkInformation> netInfos = new ArrayList<>(diff.unChanged);
        List<NetworkInformation> newNetInfos = new ArrayList<>();
        for (String ifName : diff.added) {
            NetworkInterface intf = NetworkInterface.getByName(ifName);
            if (intf == null) {
                continue;
            }

            List<InetAddress> interfaceAddresses = Collections.list(intf.getInetAddresses());
            IPAddress[] ipAddresses = new IPAddress[interfaceAddresses.size()];
            for (int i = 0; i < interfaceAddresses.size(); ++i) {
                InetAddress inetAddress = interfaceAddresses.get(i);
                ipAddresses[i] = new IPAddress(inetAddress.getAddress());
            }

            NetworkInformation netInfo = new NetworkInformation(ifName,
                    NetworkChangeDetector.ConnectionType.CONNECTION_ETHERNET,
                    NetworkChangeDetector.ConnectionType.CONNECTION_NONE, TETHERING_NETWORK_HANDLE,
                    ipAddresses);

            newNetInfos.add(netInfo);
        }

        netInfos.addAll(newNetInfos);
        tetheringNetworkInfo = netInfos;

        for (NetworkInformation connNetInfo : newNetInfos) {
            observer.onNetworkConnect(connNetInfo);
        }

        for (NetworkInformation discNetInfo : diff.removed) {
            observer.onNetworkDisconnect(discNetInfo.handle);
Reply all
Reply to author
Forward
0 new messages