Monthly Archives: June 2015

Delivering discovery data via Wi-Fi Direct UPnP

For the tests I was using WifiUPnPTestApp  and I was adding service data into the services array with WifiP2pUpnpServiceInfo. The data sent via service lines was strings containing only number characters.

I was only using two Nexus 5 devices with these tests, one of them was having 5.1.0 Lollipop and the other one was having 4.4.4 KitKat firmware.

The data that can be sent with the API are defined in the API docs for WifiP2pUpnpServiceInfo.

From initial tests I concluded that the API does require the UUID and device to be set into it. The UUID also need to be in format defined for it, thus it cannot really be made any shorter. To be on safe side I also assumed that some parts of the API might also require the device string to have the correct formatting, i.e. to be in the form of ‘urn:schemas-upnp-org:device:DEVICE_NAME’. With these tests the DEVICE_NAME was set to be one character long to save up some space for the payload.

There appears to be also a requirement for the characters that can be used with any of the strings used with the API. The defined characters can be found from the API docs . Easiest way to be sure that only supported characters are used, would be to simply Base64 encode the payload strings, which again reduces the effective payload that can be sent over.

By default there are three lines which gets delivered on top of the services advertised:

  • One line having just the UID
  • One line having the UID and saying that its upnp rootdevice
  • One line advertising the device line set for the service

Each line delivered separately to the callback, also each line added to the services array will be getting delivered separately to the callback.

Thus if the data would need to be combined, then there should be identifier included to determine which part the data is. In general it appear that last added line will be delivered first, indicating that there is a LIFO (Last in first out) buffer implemented for the UPnP advertisement sending in the API. But I would not recommend relying on the delivery order when combining the data send over with the API.

Do remember that with each separate line that is delivered, there is additional 43 byte header (for example: ‘uuid:6859dede-8574-59ab-9332-123456789011::’) added. This header will be counted in total bytes delivered and thus it will reduce the max payload we can deliver through this channel. And therefor if you want to deliver max amount of data, then the as much data as can be added in on service line, should definitely be added there.

The actual data that gets sent out from the device appears to be limited to some value less than 1K. With Lollipop tests I managed to get at max 946 bytes payload out (946 bytes +43 bytes of header = total string length = 988 bytes) in a single service line.

With KitKat the maximum data that can be delivered with one service line appears to be 188 bytes (231 -43 header), anything on top of that gets cut away. Then for maximum data you can have 4 full lines giving the payload maximum value of 752 bytes (4 x 188).

Do note that even when the KitKat device did cut any service line that was having more than 188 bytes of payload, it can see the service lines advertised by Lollipop device, even when the maximum of 946 bytes of data is used.

The maximum payload, as well as the max-line cutting appears to be handled by the LIFO buffer manager, and first it just checks the last line from the buffer and sees whether it first into the limits posed. And if it does not then noting gets sent out. If it fits then it’s added to be delivered to other devices. After the first line, it just checks next lines in the LIFO buffer until the length of already added lines + the new line would be too long, then it simply ignored the next line and anything after it.

There appears to be also some additional headers which are only visible internally, since if I want to see also the device line same time as I see my own data, I do need to reduce the size of the payload to 868 (78 bytes less), even when the length of the 1 character device name line is only 72 bytes (for example: ‘uuid:6859dede-8574-59ab-9332-123456789011::urn:schemas-upnp-org:device:A’).

Then if I also want to see the ‘One line having just the UID’ (41 bytes), and the root device line (58 bytes), then the max payload drops to 757 (189 bytes less than the max). So as with 1 line the reduction was 6 bytes, and with 3 lines it was 18, I would want to assume that each line adds firstly the 43 bytes of visible header, and then also 6 bytes of invisible header, thus actually reducing the max payload we can deliver through the channel for 49 bytes per line.

Additionally the LIFO buffer, as it is managed by the Wi-Fi Direct API, it’s also shared between different apps. Which effectively means that the app that its services added lasts to the API, will be the one that get them out, provided that they do fit into the limits posed.

The service lines added by other apps, will get out only if their size requirements along still fit, after the lines that do get added before them.

This means that the data delivery through this channel is not really reliable, since any other application can effectively turn off the discovery, and the application has no ways on knowing when that happens.

Wi-Fi Direct and Dns-Sd Txt record size measurements

I was needing a way to deliver a bit more data on the discovery phase, thus did start looking into what I can I get delivered to the DnsSdTxtRecordListener.

I decided to use the good old Power test app, as a base for the test and simply add some record on the advertisement, and then some codes to have some logging for the stuff that the device can see.

I was using the records in a way that each record has key that is 1-character long, and then each record has max 200 characters in them. Thus I would start the test with one full length record, and keep on adding them until the receiving device wont see receive the record anymore.

I would also monitor the service discovery with DnsSdServiceResponseListener same time. Also when the device is not discovering any services, I did switch the application on/off, WiFi on/off and also did reboot devices time-to-time, just to make sure that the device really can not see things.

Note also that the instance name used in test was set to “powerTests”, and quick tests with Nexus 5 with KitKat 4.4.4, did actually show that if the instance name is longer, then the data needs to be shorter, and visa versa.  Thus when checking the data, do keep this in mind.

I did not test whether utilizing different sized servity type strings, the one used was “_BT_p2p._tcp”, anyway, I did notice from the first tests that I’m not getting the DnsSdTxtRecordListener called if I use service type when creating instance of WifiP2pDnsSdServiceRequest, while starting the discovery.

For the Initial tests I was using my Nexus 4 and Nexus 5 devices, as well as my new Nexus 6. And here’s the data.

KitKat devices (checked with Nexus 4 and Nexus 5)

  • can only have 88 bytes of data in the record, if there is more, it’s not visible to the outside world.
  • can see records having 951 bytes of data, but then only DnsSdTxtRecordListener is called and DnsSdServiceResponseListener is never called for the device (did not really check if this causes any other ill effects)
  • if the record they see is at max 920 bytes of data then both callback are called just fine.

Lollipop devices (checked with Nexus 4 and Nexus 5)

  • can only have 951 bytes of data in the record, if there is more, it’s not visible to the outside world.
  • can see records having 951 bytes of data, but then only DnsSdTxtRecordListener is called and DnsSdServiceResponseListener is never called for the device (did not really check if this causes any other ill effects)
  • if the record they see is at max 920 bytes of data then both callback are called just fine.

Lollipop (with Nexus 6)

  • can only have 951 bytes of data in the record, if there is more, it’s not visible to the outside world.
  • can see records having 940 bytes of data, but then only DnsSdTxtRecordListener is called and DnsSdServiceResponseListener is never called for the device (did not really check if this causes any other ill effects)
  • if the record they see is at max 915 bytes of data then both callback are called just fine.

The data means that with KitKat devices, when using 10-character long instance name, you can only have 88 characters long record, if it is longer then its not delivered to other devices. And with Lollipop this is more than 900 characters.

Note that KitKat devices can indeed receive longer records than they can advertise, but both Kitkat and Lollipop have another limit, which after the DnsSdTxtRecordListener is called, but the DnsSdServiceResponseListener never gets called. I have no idea for reasons of this behavior, but would recommend not to exceeding the limit, since the API has shown earlier that when it gets upset, then it starts doing nasty things.

I did Quick tests with Samsung Galaxy Ace 3 (4.4.2 API level 17) device, as well as Nokia X2 (API level 18) device, and those were behaving pretty much like the Nexus devices with KitKat on them.

I also did quick tests with Samsung S4 Mini (With Cyanogenmod KitKat 4.4.4) device, and surprisingly, it behaved more-or-less just as the Nexus devices with Lollipop. Thus I’m assuming this thing might not be as straightforward as the data might suggest, and different devices might behave different ways.

The results were really disappointing, since the max data I can actually get through there is less than I get with just using the instance-name alone.

I really hope that I made some mistake on the tests, and if you find it, please let me know and I’ll redo all tests.

Basically I was expecting quite much more data to be able to go through there, at least the docs for WifiP2pDnsSdServiceInfo  do link to : http://files.dns-sd.org/draft-cheshire-dnsext-dns-sd.txt, and there it says first through that the size should be kept small, but then it also says that “Using TXT records larger than 1300 bytes is NOT RECOMMENDED at this time”, and I would be loads happier getting 1300 bytes through, than just the 88.

WI-Fi Direct final results for Discovery

I was looking into the results I was getting while testing the WiFi off/on toggling, and noticed that the service discovery is rather slow with them. thus went back and tried different things to improve the rate.

Finally I actually only added one line, that was to start peer discovery again, right after I got the final service discovered. The rate I’m getting the services discovered with this changed version is more than double when comparing Nexus 5 lollipop results. Also the battery consumption seemed to drop a bit again.

However I did finally notice that with lollipop devices, the battery consumption did increase in the end of the test, first I was thinking that there is something wrong with my measurements, until I finalized that its indeed consuming more power when its not finding any peers.

Thus I went to the middle of the forest (actually it was my Brother’s summer house) and did repeat the power consumption tests. Running one device at any time, so they would not find any devices nearby. and here’s the results showing how many hours it took to drop 10% of the battery status:

Battery N5_501 N4_501 N5_444 N5_443
100% 0,00 0,00 0,00 0,00
100% to 90% 3,00 2,00 2,67 2,67
90% to 80% 2,33 2,33 2,33 2,33
80% to 70% 2,00 2,67 2,67 2,33
70%  to 60% 2,00 2,33 2,07 2,33
60% to 50% 2,00 2,33 2,33 1,67
50% to 40% 2,33 2,33 2,33 2,33
40% to 30% 2,00 2,33 2,33 2,33
30% to 20% 2,00 2,00 2,00 1,67
20% to 10% 2,33 1,67 2,33 2,33
10%  to 0% 2,00 1,67 2,33 2,33
average 2,20 2,17 2,34 2,23

 

So, we could see that in general it takes around 2,2 hours to drop the 10% of the battery with both Nexus5 and Nexus 4, thus the runtime from 100% to 0% battery would be just around 22 hours and it would not matter whether you use KitKat or Lollipop.

Then lets see the original tests, where I did have all devices running same time:

1st result N5_501 N4_501 N5_444 N5_443
100 0,00 0,00 0,00 0,00
100% to 90% 14,96 7,43 2,67 2,67
90% to 80% 6,33 5,86 2,33 2,34
80% to 70% 5,91 5,48 2,33 3,17
70%  to 60% 5,00 6,49 3,32 2,54
60% to 50% 3,75 1,67 2,01 2,07
50% to 40% 3,50 2,33 2,33 2,27
40% to 30% 2,00 1,75 2,58 2,70
30% to 20% 1,67 2,33 2,00 1,67
20% to 10% 2,33 2,00 2,96 2,00
10%  to 0% 2,00 3,00 2,00 2,00
Average 4,75 3,83 2,45 2,34

 

With KitKat we don’t really see too much difference, they would last around 24 hours from full battery to 0%. But if we look into the drop values with Lollipop, and check the times when the KitKt devices were still around, we could estimate the 10% drop in battery status to take something between 6-8 hours.

So, instead of giving any actual values for the power consumption, I can only say that it depends on the usage situation, and whether there are peers in the vicinity to discover, anyway the results would give the Nexus 4 and Nexus 5 devices estimated time it would run from full battery untill its empty and if it would only be running the WiFi Direct discovery to be:

  • 22-24 hours with KitKat
  • 22-60/80 hours with Lollipop

The discovery rate calculated from the last results was then, that in average the time between each full discovery with Nexus 5 devices using KitKat firmware is 17 seconds, and device using the Lollipop it was 72 seconds.

The full discovery being the time started when we first call Start Peer discovery, until we get the last Service in the vicinity discovered. Thus the with each round the last actually discovered service was discovered 5-10 seconds before (its the timer  with 5 second + 0 to 5 sec random time), and the first could be discovered (Peers available * (5 to 10)) seconds before the time value. This meaning that if the implementation decides to use the discovered services before the full list is available, the discovery would indeed work loads faster.