rosjava / android_core

Android libraries for rosjava
146 stars 166 forks source link

Resolving multiple network adapters on a phone #147

Open chadrockey opened 11 years ago

chadrockey commented 11 years ago

Original author: markus.b...@gmail.com (October 12, 2012 19:10:07)

InetAddressFactory.newNonLoopback() returns non-wifi network adapter on a phone

What steps will reproduce the problem?

  1. Change android_tutorial_pubsub to use NodeConfiguration.newPublic(InetAddressFactory.newNonLoopback().getHostAddress()); instead of NodeConfiguration nodeConfiguration = NodeConfiguration.newPrivate();
  2. Build android_tutorial_pubsub.
  3. Upload and execute it onto a android phone.
  4. Start it.
  5. Enter the ROS master URI
  6. Hit "OK"

What is the expected output? What do you see instead? App PubSubTutorial should connect to the masters URI and start publishing Messages. It should do so and use its wifi ip address in the topics information.

It does connect to the ROS master, but it propagates its UMTS/GSM ip address. This is, in my case, vsnet0 instead of wlan0. This ip address is obviously not useful for other nodes as they are unable to connect to it.

What version of the product are you using? On what operating system? Latest rosjava_core and android_core from 10.October 2012Android 4.1.1 . I do not know how to extract the exact version info from my installation.

Please provide any additional information below. vsnet0 is a PointToPoint interface, wlan0 is not. The attached patch checks if the first nonLoopback interface is a PointToPoint one. If not it should be the desired wifi interface.

Original issue: http://code.google.com/p/rosjava/issues/detail?id=147

chadrockey commented 11 years ago

From markus.b...@gmail.com on November 06, 2012 14:32:29 Please see http://code.google.com/p/rosjava/issues/detail?id=150 for an updated patch for this issue.

Sorry for opening another issue for the same problem. Markus

stonier commented 10 years ago

This is regularly becoming a problem given phones often now utilise multiple interfaces at once. Problem code is in:

NodeConfiguration nodeConfiguration =
                NodeConfiguration.newPublic(InetAddressFactory.newNonLoopback().getHostAddress(), getMasterUri());
nodeMainExecutor.execute(rosTextView, nodeConfiguration);

This code in the factory:

  public static InetAddress newNonLoopback() {
    for (InetAddress address : getAllInetAddresses()) {
      // IPv4 only for now.
      if (!address.isLoopbackAddress() && isIpv4(address)) {
        return address;
      }
    }
    throw new RosRuntimeException("No non-loopback interface found.");
  }

results in just the first one listed getting lucky. Some information from the official java docs:

Multiple interface/address configurations were relatively rare when this API was designed, but multiple interfaces are the default for modern mobile devices (with separate wifi and radio interfaces), and the need to support both IPv4 and IPv6 has made multiple addresses commonplace. New code should thus avoid this method except where it's basically being used to get a loopback address or equivalent.

There are two main ways to get a more specific answer:

If you have a connected socket, you should probably use getLocalAddress() instead: that will give you the address that's actually in use for that connection. (It's not possible to ask the question "what local address would a connection to a given remote address use?"; you have to actually make the connection and see.)

That getLocalAddress function is part of the Socket API.

Maybe what we can do is connect to the master, get the socket it is using and query that to work out what interface our phone has routed on?

stonier commented 10 years ago

Testies (albeit only one)! The following is some code from the implementation of a RosActivity init.

    protected void init(NodeMainExecutor nodeMainExecutor) {
        try {
            java.net.InetAddress local_network_address;
            java.net.Socket socket = new java.net.Socket(getMasterUri().getHost(), getMasterUri().getPort());
            local_network_address = socket.getLocalAddress();
            socket.close();
//        Ye Olde:
//        NodeConfiguration nodeConfiguration =
//                NodeConfiguration.newPublic(InetAddressFactory.newNonLoopback().getHostAddress(), getMasterUri());
            NodeConfiguration nodeConfiguration =
                    NodeConfiguration.newPublic(local_network_address.getHostAddress(), getMasterUri());
            Talker talker = new Talker();
            nodeMainExecutor.execute(talker, nodeConfiguration);
            nodeMainExecutor.execute(rosTextView, nodeConfiguration);
        } catch (IOException e) {
        }
    }

This works. @damonkohler can you think of cases where this would actually be a problem? Is this a method we can add to the InetAddressFactory suite? Or even better, bury under the hood of the NodeConfiguration constructors.

damonkohler commented 10 years ago

Unfortunately, just using the address that is used to connect to the master isn't good enough. For example, the master may be local and accessible via loopback but other nodes will be off the system and not accessible that way.

I don't think there's a good answer to this. The newNonLoopback() method is really just for convenience. I don't think it's necessarily rosjava's job to find the right address every time. That could lead to a lot of lag/overhead without some rather complicated/clever connection code.

However, I could see adding a drop down to the MasterChooser that would assist the user in selecting the correct interface.

stonier commented 10 years ago

Unfortunately, just using the address that is used to connect to the master isn't good enough. For example, the master may be local and accessible via loopback but other nodes will be off the system and not accessible that way.

Ah, I was mistaking the host address as being purely for negotiating with the ros master. Instead, this is like setting ROS_IP for a node. Your suggestion of implementing it in the master chooser is a good one.