In my earlier blog post I discussed data sharing between two android devices in same network using NSD. In this post we will see communication between two non-connected android devices (can be connected to same or other network, doesn’t really matter) via WiFi direct. Devices should be in WiFi range. I will start with a bit of theory about WiFi direct and then we will see how it is implementable using android APIs (Sample app source code git link at the end of post).
Same as earlier post the problem addressed is sharing IP and port information. Communication again will be socket communication.
WiFi direct is a WiFi certified standard enabling devices to connect with each other without requiring a wireless access point (a.k.a router or WiFi hot spot). Using this, devices can communicate with each other at typical WiFi speeds, unlike adhoc wireless connections (which allows two or more devices to connect and communicate, and has a limit of 11 Mbps) or Bluetooth, setup required for WiFi direct is much simpler. Here each member is assigned a limited access point and other members connect to it as regular clients. WPS and WPA2 are used for encryption and keep the communication private.
The peer device acting as current access point is said to be assuming a group owner role in WiFi direct group. A WiFi direct group consists of 1 group owner and devices or peers connected to it as clients (P2P clients). The group owner device sets properties for communication like operating channel, whether the group is persistent, encryption type etc. Any compatible device with right hardware and android ICS or above (API 14+) can assume group owner role. After role negotiation (group owner or P2P client), devices assume their decided role, and group owner starts operating in access point mode (this access point will not be visible under available networks of mobile devices).
Now coming to android. Android’s WiFi P2P framework complies with WiFi direct certification program. It consists of:
- Methods that allows us to discover, request and connect to peers (android.net.wifi.p2p.WifiP2pManager).
- Listeners that notifies us of success and failure of WifiP2pmanager’s method calls.
- Intents to notify specific events, such as new peer joined, connection dropped etc. (like WIFI_P2P_PEERS_CHANGED_ACTION and WIFI_P2P_CONNECTION_CHANGED_ACTION)
Discovering peers
You need the following permissions for using WiFi direct for communication
"android.permission.INTERNET" "android.permission.ACCESS_WIFI_STATE" "android.permission.CHANGE_WIFI_STATE"
Internet permission is required for using sockets anyway.
wifiP2pManager.discoverPeers(wifip2pChannel, new WifiP2pManager.ActionListener() { @Override public void onSuccess() { // discovery has been successfully started } @Override public void onFailure(int reasonCode) { // discovery failed to start. checkout reason code } });
The above method is all you need to start the WiFi direct peer discovery. If you get a callback in failure method, check the reason code for it.
Getting peer list
Getting peer list is tricky, as WifiP2pManager will not give you that. This is done via broadcast receivers registered dynamically (not in the manifest file).
The second filter action in above code is what is triggered when a new peer has joined (or left). When broadcast with this action is received we can request peers from WifiP2pManager. It takes a callback to return entire peer list not just the new ones, so if you are displaying a list, clear the list and add all the peers received in the callback. here is a portion of the broadcast receiver’s code:
if (WifiP2pManager.WIFI_P2P_PEERS_CHANGED_ACTION.equals(action)) { // request available peers from the wifi p2p manager. This is an // asynchronous call and the calling activity is notified with a // callback on PeerListListener.onPeersAvailable() of passed activity // the activity implements the listener interface if (wiFiP2pManager != null) { wiFiP2pManager.requestPeers(channel, activity); } }
The callback that is overridden will be called asynchronously. Peer list can be extracted from there.
@Override public void onPeersAvailable(WifiP2pDeviceList peerList) { List<WifiP2pDevice> devices = (new ArrayList<>()); devices.addAll(peerList.getDeviceList()); //do something with the device list }
So now we have the peer list. We can display this list to user or call connect with each peer. Once the peer is discovered we need to send a connection request to connect and form a WiFi direct group.
Connecting with a peer
This step requires WiFi mac of the device. It is received as a part of peer info in discovery step. Here are the properties of WifiP2pDevice:
The WifiP2pDevice list that we received in OnPeersAvailable() method of PeerListListenercallback has exactly what we need to make a connection request.
Here is how to make a connection request using WifiP2pManager.
WifiP2pConfig config = new WifiP2pConfig(); config.deviceAddress = //set device address from WifiP2pDevice received; config.wps.setup = WpsInfo.PBC; config.groupOwnerIntent = 4; wifiP2pManager.connect(wifip2pChannel, config, new WifiP2pManager.ActionListener() { @Override public void onSuccess() { // Connection request successfully sent } @Override public void onFailure(int reasonCode) { // Failed to send connection request. } });
Just create a config object set channel properties and call the connect method. The group owner intent value is for setting the probability of the device to become group owner. It’s value varies from 0-15. 15 means highest chance of the connection request sender device to become group owner. But whatever code you write, you must handle both group owner and a regular P2P client scenario.
Once the request is sent to a device, the user must accept the connection request from system prompt. After that role negotiation happens and devices move to their decided roles. After successful connection, a connection info request can be issued via WifiP2pManager. Which sends callback to WifiP2pManager.ConnectionInfoListener. It has only one method:
This connection info object WifiP2pInfo contains the IP information of group owner. Check below:
So now, our first problem is addressed, here we got the IP address of the group owner. And we also know whether the current device is group owner or not. So basically every peer in the group knows the group owner’s IP address.
The remaining problem is sharing the port it is listening on. I couldn’t find any proper solution for this, I solved it by prefixing the port, then sharing the dynamic port data with clients, and then clients can share their own info and dynamic port. So first communication happens over the fixed port and after that, then dynamic port data number is transferred, and then it is regular socket communication. Check out my sample app for this.
Even this problem can be solved using the WiFi direct service discovery. Watch out for my next post.
One last thing to cover in this post is method called createGroup(). This method is for supporting the legacy devices with no WiFi direct hardware. This basically creates an access point and any device with WiFi capability can connect with it like a regular access point and share data like we did in my previous post. On top of that it can work as expected in WiFi direct scenario, so supports both type of devices.
Sample app code available in github.
Happy coding!!!