Monthly Archives: February 2015

The latest version of the test application can be found from DrJukka/P2PChatTest will be updating it until I’m happy with it, or until I make new app to do tests with.

The timing is based on test with the application doing the loop described in Initial investigation post here, and the measured points are as follows:

  1. FoundService
    • Actually, found peers. The time between issuing requestPeers(), and adding service request for service discovery
  2. StartServiceDiscovery
    • Time between adding service request, and success() callback called for starting the service discovery (1 s delay removed)
  3. GotService
    • Time between starting the service discovery and finding suitable service.
  4. Connecting
    • Time between finding the service, and onSuccess() for connect being called.
  5. Connected
    • Time between starting the connection, and actually getting event for being connected.
  6. GotConnectionInfo
    • Time between requestConnectionInfo() call and getting the information.
  7. GotData
    • Time between having the connection information, and receiving first data.
  8. GotBigData
    • On group owner side, time to receive1 Mb of data, on client side the time to write that data to a socket.
  9. FromDcToSd
    • Time between the last data retrieval, and the event informing that we are disconnected.

Note that the data only has values for loops that went through from beginning all the way to getting the big data, any loops that did not result receiving/sending the megabyte data are not logged. Also some logs did not have values for all connections steps. In those cases the reason fro missing data, is that the connection was initiated by the other party, and we just got information that we are now connected.

I also divided data between Group owner and client, since they are doing a bit different functions. All data is in milliseconds, and disconnection method used here, was to turn off the device after the full cycle was completed.

Here’s Group owner data:

WD_GroupData

Most of the time there is spend on waiting the disconnection event, this is party effected by user not disconnecting the device immediately, but in main parts of it are really caused by the API being slow on determining that the connection was lost.

Then we can also see that the Service discovery is taking on average 4 seconds, and in worst case it took nearly 20 seconds.  And for creating the connection took 6 seconds on average, and worst case it took over 12 seconds. From the data we could determine that in worst case, it could take over 40 seconds between the device was discovered, and before we are getting first data

For Client the data looks rather same:

WD_ClientData

Though service discovery looks loads better and the worst case is only taking a bit over 3 seconds and also the average is taking over 60 % less time than when we were group owners. Connecting them takes about the same time. All and all, with Client with worst case we get first data through in half time when compared to the group owner side

Here’s quick pie charts to illustrate the differences in graphical form for the average times measured:

WD_AverageGroup WD_AverageClient

This does indeed give nice overview on the timings, though I have to admit that the sample size should be bigger, as well as different scenarios should also be taken account to see how the API behaves is them.

Initial Investigation with Wifi-Direct

I started the work by just having two Nexus 4 phones, on having 5.0.1 Lollipop and other 4.4.4. Kitkat, soon noticed that it’s not really best configuration, since there has been loads of improvement done to the API between there versions, and some issues might not be present with all versions. Thus I did get 3 additional Nexus 5 devices for testing purposes. The firmware’s on these were, 5.0.1, 4.4.4 and 4.4.3

The API used is WifiP2pManager   which is actually a system service, thus anything done with it, will effect on any other apps utilizing the same service. Also utilization of Wi-Fi with other means, might affect the usage, but in this point. The Investigation is made having the application being the only app using the service, as well as without having any WLAN connectivity available.

First I wanted to get application which loops connection & data exchange between two devices, so the behavior would be:

  1. Discover service from other device
  2. Connect to the service on the other device
  3. Exchange data
  4. Disconnect from the other device
  5. Start discovering the service

Managed to get this working quite a well, though with Nexus 5 which was having KitKat 4.4.3, there were still loads of issues, and if that device was trying to communicate any device having KitKat on it, then the connection establishment would fail really often, and the actual successful data exchange was happening rarely (at least when compared to communications between 4.4.4 and 5.0.1 in any configuration), thus if you have KitKat 4.4.3 on your device, please update it to 4.4.4 for more pleasurable experience with the WifiP2pManager.

  1. Advertise your service

In order the other device to be able to discover the service you have, you must advertise it. To do this you simple first create an instance of WifiP2pDnsSdServiceInfo with your own service type and unique instance name, so you can identify your own services, and then use addLocalService() to add it to the WifiP2pManager.

Do remember to remove the local service when you exit, so it won’t be advertised while you are not running the service.

  1. Discover service from other device

For discovering step, we could indeed go directly discovering any devices that are having the service we are interested call discoverServices() function, and make sure the discovery is running until we get onDnsSdServiceAvailable() function from  DnsSdServiceResponseListener() called with service we are interested in. I indeed did do this first, but decided to go back to the ‘normal’ way and first discover devices as peers, and once I have found devices, then would I start service discovery. Reasoning was that, supposedly the service discovery uses more battery, but also I did find that keeping the peer discovery active & running nicely, is loads easier than having the service discovery doing the main tasks in discovery step.

Peer discovery works rather simple way, you just call discoverPeers() and once the onSuccess() is called you are happily running. And If you did register for WIFI_P2P_DISCOVERY_CHANGED_ACTION, you’ll get notification for state changes, and can react on them. And once there is changes on peers discovered (more found, or lost), you’ll be informed via WIFI_P2P_PEERS_CHANGED_ACTION, and you can simply call requestPeers() to get the latest list of peers.

There are few issues though that you might want to take care of

  1. The discovery can be started by any application at any time and WIFI_P2P_PEERS_CHANGED_ACTION events are then given to all parties, so even if you don’t start the peer discovery, you’ll be getting the events
  2. You also might get WIFI_P2P_PEERS_CHANGED_ACTION events when your logic is handling something else, for example connecting, discovering services, thus you need to have logic to take care of those situations.
  3. Note that you do get WIFI_P2P_DISCOVERY_CHANGED_ACTION with WIFI_P2P_DISCOVERY_STOPPED when you are connecting to the other device, thus if you attempt to call requestPeers(), you’ll get:
    1. BUSY error with KitKat 4.4.4 and newer firmware versions, which is good,
    2. the peer discovery started just fine, and without any errors, when using KitKat 4.4.3, this will likely mess up your connection attempt, thus should be avoided.
  4. With KitKat requestPeers() might in some random cases cause onFailure() being called with generic zero error. And to make sure your logic works, and devices are being discovered, you should consider implementing watchdog feature, which would go starting peer discovery, once it determines that it has taken too long since the last peer was discovered.

Note also that onPeersAvailable() for WifiP2pManager.PeerListListener() will be called when all peers are lost, thus for your logic, at least you should check whether there were any peers discovered before starting service discovery, of course you can also check the peer list, and determine whether you should start service discovery or continue looking for right mates. Anyhow, with my simple app, the logic just checks whether there is any peers available, and if there is, then we start querying the services strait away.

Before starting the Service discovery, you first need to create a new instance of WifiP2pManager.DnsSdServiceResponseListener() and use setDnsSdResponseListeners() to tell to the WifiP2pManager where you want the service discovery results being delivered.

The service discovery process is handled in three steps:

  1. Create a service requests by creating a new instance of WifiP2pDnsSdServiceRequest and then add it to the service by calling addServiceRequest(), and once onSuccess() callback fro the action listener is called, then move to step 2.
  2. Call discoverServices(), and if the onSuccess() is called, then just wait until the API takes you to step 3.
  3. Once there is services running in nearby devices, and they were discovered, then the onDnsSdServiceAvailable() from WifiP2pManager.DnsSdServiceResponseListener() will be called and you can check whether the service you are looking (the one you are advertising, with your own service type) for is available and then start connecting.

Then again some issues were discovered, and here’s short list of them:

  1. There were some race condition discovered earlier, thus you should have the suggested 1 second delay between addServiceRequest() and discoverServices() to avoid random NO_SERVICE_REQUESTS error
  2. With Kitkat also when using the RemoveGroup() as disconnection method, the discoverServices() will end up giving NO_SERVICE_REQUESTS really often, and only way to get rid of it, was found to be to switch off WLAN, and once the state change was reported back, then switch it back on. Not then that:
    1. I never seen this happening with Lollipop
    2. With Kitkat this often leads to local Service being lost (i.e. the discovery stopped working on the other end), thus you should clear local services before switch off, and add them back once you switch the WLAN back on
  3. With KitKat, you also randomly get generic zero error with discoverServices() call, and the suggested fix is the same as with NO_SERVICE_REQUESTS error.
  4. With KitKat calling discoverServices() when you have active service discovery already, appeared to confuse the service time-to-time, thus, would suggest cancelling any previous service discoveries, before starting new ones.
  5. With KitKat 4.4.3 sometimes the service discovery appears to not find any service, and with tests the watchdog resets the discovery after 2 minutes of idle time.
  6. Connect to the service on the other device

Once you have discovered the service from the device you want to connect to, you simply do following:

  1. Call connect() function with WifiP2pConfig instance having the device address (given you via device argument in onDnsSdServiceAvailable()). If the onSuccess() is called for the action listener, then move to step 2.
  2. Once connection is established, you’ll receive WIFI_P2P_CONNECTION_CHANGED_ACTION event. Note that this comes also if the connection failed (you get disconnection event), thus check whether the EXTRA_NETWORK_INFO indicates that you are connected, and if you are then call requestConnectionInfo().
  3. Once the information for the connection is ready, it will be delivered via ConnectionInfoListener interface. And with the WifiP2pInfo argument, you can determine whether your device is Group owner or client, and you can then act accordingly.

With the Connection establishment there were no real issues, exception being connections between KitKat 4.4.4 and 4.4.3, which with the behavior experienced was as follow:

  1. Device’s discover each other
  2. The both get right service, and start connecting to it
  3. The connection is never connected, and watchdog kicks in to re-start the discovery process.

 

  1. Exchange data

Once the onConnectionInfoAvailable() of ConnectionInfoListener is called, you can get the IP address of group owner from the WifiP2pInfo given as argument. With Client simply connect to that address, and with Group owner, you could either start listening connections in this point, or you could have had started the listening processes earlier.

No real issue noted here, though, with current tests all tests were handled in a way that the devices were stationary, and disconnections happened only when data was exchanged. In reality clients could go out of range at any moment, thus the data exchange might fail really easily.

  1. Disconnect from the other device

Currently the only reliable way from coding perspective to do disconnections has been to remove the group from the group owner’s side. This would disconnect the connection strait away in group owner’s side, and after some seconds delay also from client side.

In real world the disconnection would generally happen when devices go out of range, and with testing time, I so far simulated this by switching devices off.

Only notable issue here is that when you remove the group, the data exchange stops, and if you do the removal right after sending data, then there is really high risk that the data never arrives into the other end.

Notable thing also is that you might want to avoid disconnecting in onConnectionInfoAvailable() function, since group owner seems to get that first, thus doing group removal there, will mean that once client gets the function called, the information given will be invalid.

  1. Start discovering the service

As a last step, you just need to go back to the first step, and start the peer discovery again. In general you would do this once you get the WIFI_P2P_CONNECTION_CHANGED_ACTION and determine that you don’t have connection. You’ll get here once your connection was disconnected, or the connection request lead into situation where the connection was not established.

Quick Status update

It’s been awhile since I managed to get anything written here, I have been going through some transformation on my work as well as on my development interests.

I finally got to drop main parts of my Symbian source codes into the GitHub. The stuff that s not there, is likely not going to get there, since there might be issues of code ownership etc.

The organization of things there is at best messy, but Do note that there should be sis files (also signed ones), thus if you need them, just browse through and see what’s in there. No support really given, though if you ask at Stack overflow, I’ll probably find it from there and do answer still.

I do have some WinRT & Windows phone projects that are semi-ready, though I’m having feeling that they are not getting any development time any day soon, so will be considering on dropping them here once I find some time.

Meanwhile I’ll be doing some Android stuff, its been awhile since I did any, and that was with Nokia X devices, and using Eclipse IDE. Thus starting this time also mean getting used to new tools, so there were a bit of catching up to do.

All and all was happy to get back on doing some investigative programming, which should lead into some usable source code examples, and hopefully some really usable discoveries and something that could be used in other projects as well.

The first task to do, is described in: http://thaliproject.org/AndroidP2P. My plan would be to go through Wifi & BT parts from there and I’ll be starting the investigation with Wifi-Direct API. The questions needing answers in there are:

  • Can we discover peers while in stand by mode?
  • Can we be discovered by peers while in stand by mode?
  • Can we discover services while in stand by mode?
  • Can we be discovered by peers looking for a service on our device while in stand by mode?
  • How long does a device have to be nearby before we will discover it?
  • What is the power consumption for supporting advertising ourselves and being discovered while in stand by mode?
  • How long does it take from discovering a device to delivering the first byte?
  • What is the effective bandwidth between devices?
  • Once we join a particular peer and get user confirmation, do we have to get user confirmation for future joins?
  • There are at least two race conditions that Michael found in the API, we need to identify them and see if there are more.
  • Can the client use Wi-Fi Infrastructure and Wi-Fi direct at the same time? It’s supposed to work, but we should check.