?

Log in

Previous Entry | Next Entry

Preface
The procedures described here are for peers discovery over Wi-Fi Direct network. Although they may be used for auxiliary purposes, it's almost impractical to apply them to real apps. Finding a peer is only demonstrates its ability to communicate, but without knowing the capabilities of the peer, such a communication will be like the conversation without a subject. To discover the services advertised by peers and to talk to them on particular subject, use Service Discovery (mDNS and DNS-SD).

Background
Wi-Fi Direct became very popular recently because
a) it has a minimal and relatively low-cost hardware requirements, namely only use of 802.11g and
b) it may be implemented entirely in software over traditional Wi-Fi radio waves.
In contrast with traditional Wi-Fi networks, where clients discovered and associated with pre-defined AP (Access Point), a major novelty of Wi-Fi Direct is that these roles are specified dynamically, and hence a Wi-Fi Direct device has to implement both the role of a client and the role of an AP (Soft-AP).
Wi-Fi addressing basics. Any Wi-Fi adapter, or more precisely, its antenna receives radio-waves in pre-defined ICM band (usually 2.4 HGz). Since the antenna can not reject the packages targeted to specific device, it's a task of the wireless nic to capture  only the packages which destination is its device. Actually, nic (Linux-driver) supports 6 operation modes.
In Wi-Fi Direct realm, group is equivalent to traditional Wi-Fi network. The device implementing AP-like functionality is referred to as group owner (GO).  When two devices discover each other they negotiate their roles to establish the group, however, one can claim its intents to be group owner via WiFiP2pConfig.groupOwnerIntent in the range 0-15.
Like traditional AP, a P2P GO announces itself through beacons and required to run a DHCP (Dynamic Host Configuration Protocol) server to provide P2P clients with IP addresses.
Although Android announced its support for WiFi P2P starting from ver. 4.0 (ICS), there are devices that only include the WiFi P2P API as a part of the operating system, but lack a appropriate package manager.

1. Basics
The foundation infrastructure for all Android Wi-Fi (include Wi-Fi Direct) is a bit tailored part of Linux Wireless.In particular, Android uses a modified wpa_supplicant to perform AP authentication and association. Through this service it also communicates with underlying driver.


The WifiNative class (called so probably because it's simple JNI-wrapper) is used to send various commands to wpa_supplicant, and the WifiMonitor class is used to monitor wpa_supplicant status changes and notify Android framework.
For general architecture brief, see here.
Although wpa_supplicant is well documented (also for developers and it's open source), it's worth to mention several essentials of its manual here.
First of all, its starting entry:
wpa_supplicant -c /etc/wpa/wpa.conf -D wext -dd - i wlan0 -B
-c read the configuration file from
-D driver to use
-dd very debugging verbose messages
-i wireless interface
-B run as daemon
The configuration of wpa_supplicant is illustarted here and some implementation thougths were published (Linaro for Samsung).
Also worth to jog a memmory that with a help of ADB you can manually edit this configuration on the (rooted?) device.
Finally, just to remind that Wi-Fi Direct's support is available starting from ICS (4.0) on.

2. Device Discovery
Before any connection can be established, P2P devices have to find each other. For this they alternatively listen and send probe requests with additional P2P information elements on so-called social (and non-overlapping) ISM (Industrial, Scientific and Medical) channels, which are challels 1, 6 and 11 on 2.4GHz band.
(Rumors are that there are totally 13 channels on 2.4 GHz band, and 13-th is forbidden for all countries, exclude, for example, Bolivia). Some network monitors may switch their llisteting for channels, i.e perform the procedure called "channel hopping". P2P assumes the transmitting channel is selected once and not changes for entire life-circle of communication.

During discovery and negotiation P2P devices use their global MAC-address as Device ID.
From Android perspective, this is a very standard procedure. It's a well documented and illustrated by WiFiDemo sample from Hardik Trivedi. Generally, you use various methods of WiFiP2pManager to initiate operations on wpa_supplicant and receives system intents in specially provided Broadcast Receiver.


For example, you start peers discovery by WiFiP2pManager.discoverPeers(), then wait for WIFI_P2P_PEERS_CHANGED_ACTION in broadcast receiver. From wpa_supplicant perspective, the discovery started as soon as its command P2P_FIND is specified. The duration of the discovery and the search type are optional parameters
From there you can further invoke WiFiP2pManager methods and receive the events in various listener implementations (e.g. WifiP2pManager.PeerListListener, WifiP2pManager.ConnectionInfoListener, WifiP2pManager.GroupInfoListener and so on).

An alternative procedure for discovery where the mentioned process is enriched by additional negotiation is called "service discovery". When service discovery is used instead of wider devices, the whole picture of utilizing wpa_supplicant remains unchanged, just instead of discoverPeers() the process is initiated by WiFiP2pManager.discoverServices(). To this extent, Android application can advertise its services via supported Bonjour (or Upnp - ?) protocol.

Advertising on Bonjour is very different from common NSD on established networks. Bonjour uses DNS-SD to find the published services before the connection between devices is created. Published service info get available to the peer with all details needed to create the connection (this info encapsulated in WifiP2pDevice class). If it chooses to do so - the publisher gets the invitation to accept the network.

Let's go deeper on Bonjour:

Server:
. Delete existing persistent groups
1. StartRegistrationAndDiscovery
  1a). Prepare DNS-SD service info
       
Map<String, String> record = new HashMap<>();
    record.put(Globals.TXTRECORD_PROP_AVAILABLE, "visible");

    // Service information.  Pass it an instance name, service type
    // _protocol._transportlayer , and the map containing
    // information other devices will want once they connect to this one.
    WifiP2pDnsSdServiceInfo service = WifiP2pDnsSdServiceInfo.newInstance(
            Globals.SERVICE_INSTANCE,
            Globals.SERVICE_REG_TYPE, record);
 




  1b).  call addLocalService in order to publish (start advertise) this service

2). Discover Services
  2a). Prepare DNS-SD listeners
      WifiP2pManager.DnsSdServiceResponseListener and WifiP2pManager.DnsSdTxtRecordListener

3). Prepare DNS-SD request
    mServiceRequest = WifiP2pDnsSdServiceRequest.newInstance();




  mManager.addServiceRequest(mChannel, mServiceRequest,
        new TaggedActionListener("add service discovery request"));




4). Start service discovery mManager.discoverServices(mChannel,
        new TaggedActionListener("service discovery init"));




After a while




5). Receive onPeersAvailable(), analyze status of the peer and send invitation to connect
  for(WifiP2pDevice device : wifiP2pDeviceList.getDeviceList()) {




    if( device.status == WifiP2pDevice.AVAILABLE ) {

        WifiP2pConfig config = new WifiP2pConfig();
        config.deviceAddress = device.deviceAddress;
        //config.groupOwnerIntent = 15;
        config.wps.setup = WpsInfo.PBC;
        //config.wps.setup = WpsInfo.LABEL;

        wifiUtil.connectToDevice(config, 2000); // 2 sec delay
    }
}




6). Receive onConnectionInfoAvailable() with the params of the group
  Start ServerSocker and wait to accept








3. Connection is established
After 2-nd device accepts the invitation, the connection is established, the group is created and onConnectionInfoAvailable() method of ConnectionInfoListened interface is called on both sides.It receives WiFiP2pInfo structure with group address and indicatior of group' address. Generally speaking, this groip address is enough to establish sockets connection between two devices.
Optionally, to get more information about the group, onConnectionInfoAvailable() handler may further invoke WiFiP2pManager.requestGroupInfo() passing to it the instance of GroupInfoListener implementation.
onGroupInfoAvailable() then is called on both sides with the following info (in WiFiP2pGroup)
Note that created group looks differently on client's and group owner's side. The primary difference is the passphrase which known only to group owner. The group address is also varies. The following table summarizes the differencies:

On client
(Android_279f)
On Group Owner (Android_6e08)
Group Address p2p-wlan0-0 p2p-p2p0-11
SSIS (Network Name) DIRECT-[xx]-[deviceID of Group Owner], e.g.
DICRECT-Wy-Android6e08
DIRECT-[xx]-[deviceID of Group Owner]
Passphrase Null Actual password (plain text)
Size 0 (Why?) 1 (Why?)
If you enumerate at this time (within onGroupInfoAvailable) the existing network interfaces, you'll find the newly created one that be called p2p-[prev. existing interface]-N. For example:

On client On Group Owner
Io
P2P? no
Addresses:
Loopback
::1%1 Yes
127.0.0.1 Yes
p2p0
P2P? no
Addresses:
Loopback?
fe80:8832:9bff:fe3b::d8dd%p2p0 No
wlan0
P2P: no
Addresses:
Loopback?
fe80:8a32:9bff:fe3b::d8dd%wlan0 No
10.0.0.9 No

9.p2p-wlan0-0
P2P: no
Addresses:
Loopback?
192.168.49.137 No
Io
P2P? no
Addresses:
Loopback
::1%1 Yes
127.0.0.1 Yes
p2p0
P2P? no



wlan0
P2P? no
Addresses:
Loopback?
fe80:ae22bff:fe64:5cd3%wlan0 No
10.0.0.5 No

9.p2p-p2p0-11
P2P? no
Addresses:
Loopback?
192.168.49.1 No



4. Using Sockets
Immediately after the group owner's address becomes known to the client (usually in onConnectionInfoAvailable() ), nothing can stop it to open socket on this address. The server side does the opposite procedure.Obviously both needes additional thread to deal with sockets

Server Client
public static class ServerAsyncTask extends AsyncTask {

        Context mContext;

        public ServerAsyncTask(Context context){
            mContext = context;
        }

        @Override
        protected String doInBackground(Void... voids) {

            try {
                ServerSocket serverSocket = new ServerSocket(SERVER_PORT);

                String traceMessage = "Server: Socket opened";
                Log.d(LOG_TAG, traceMessage);

                Socket clientSocket = serverSocket.accept();

                BufferedReader reader =
                        new BufferedReader(new InputStreamReader(clientSocket.getInputStream()));
                traceMessage = reader.readLine();
                Log.d(LOG_TAG, traceMessage);

                serverSocket.close();

            } catch (IOException e) {
                Log.e(LOG_TAG, e.getMessage());
          }

            return null;
        }
    }
public static class ClientAsyncTask extends AsyncTask {

        Context mContext;
        String mMessage;
        InetAddress mGroupHostAddress;

        public ClientAsyncTask(Context context,
                               InetAddress groupOwnerAddress,
                               String message){
            this.mContext = context;
            this.mGroupHostAddress = groupOwnerAddress;
            this.mMessage = message;
        }

        @Override
        protected String doInBackground(Void... voids) {

            Socket socket = new Socket();
            try {
                socket.bind(null);

                socket.connect(new InetSocketAddress(mGroupHostAddress.getHostAddress(),
                        SERVER_PORT), SOCKET_TIMEOUT);

                String traceMessage = "Client socket connected";
                Log.d(LOG_TAG, traceMessage);

                OutputStream os = socket.getOutputStream();
                os.write(mMessage.getBytes());

                os.close();
                socket.close();

            } catch (IOException ex) {
                Log.e(LOG_TAG, ex.getMessage());
            }

            return null;
        }
    }


5. Notes
Latest Android versions support the concept of "Persistent Group" or as mentioned in WiFi Direct Setting "Remembered Group". The idea behind this concept is to allow reconnection to this group without user intervention. It's really good for sequentially connected devices like home equipments, but may cause the problems with devices that assumed to estabslish new connection frequently.
The concept was described in IEEE 802.11 standard and may be briefly described as following.
During the group formation process, P2P devices can declare a group as persistent by using a flag in the Beacon frames, Probe Responses an GO negotiation frames. In this way, the device forming the group store network credentials and the assigned P2P GO and client roles for subsequent re-installation of the P2P group. Specially, after the Discovery phase, if a P2P device recognizes to have formed a persistent group with the corresponding peer in the past, any of two P2P devices can use only Invitation Procedure (a two-way handshake) to quickly re-instatiate the group.
Unfortunatelly, WiFiP2pManager.connect() method does not send the proper invitation to the peer if the corresponding persistent group is exists. May be this is due to the fact that such support was only introduced in Android 5 and lacks in previous P2P implementations.
Anyway, the documented API does not provide methods for deleting the persistent groups. In a case the deletion is really needed, one can use reflection in this manner:

Method[] methods = WifiP2pManager.class.getMethods();
for (int i = 0; i < methods.length; i++) {
if (methods[i].getName().equals("deletePersistentGroup")) {
// Delete any persistent group
for (int netid = 0; netid < 32; netid++) {
methods[i].invoke(mManager, mChannel, netid, null);
}
}
}

Must reading:
0. Absolutely must: Zero Configuration Networking: The Definitive Guide. By Cheshire S. and Steinberg D.H.
1. Device to device communications with WiFi Direct: overview and experimentations.
2. Why Wi-Fi Direct can not replace Ad-hoc mode
3. Usng WiFi P2P for Service Discovery (Google official documentaiton)
4. Wi-Fi Direct. White Paper.
5. P2Feed site.