In a conventional wireless network, devices are connected to each other through a wireless access point. With the help of Wi-Fi Direct, devices connect to each other without the need of a wireless access point. It's similar to Bluetooth, but it is faster and the range of Wi-Fi Direct is longer. New Wi-Fi Direct APIs are introduced with Android Ice Cream Sandwich which allows us to use Wi-Fi Direct properties of Android devices.
The main class that will help us to find and connect peers is the
WifiP2pManager
class. We are going to use the following Listener
classes during finding and connecting to peers:
WifiP2pManager.ActionListener
WifiP2pManager.ChannelListener
WifiP2pManager.ConnectionInfoListener
WifiP2pManager.PeerListListener
Finally, the following intents will help us in a Wi-Fi Direct connection:
WIFI_P2P_CONNECTION_CHANGED_ACTION
WIFI_P2P_PEERS_CHANGED_ACTION
WIFI_P2P_STATE_CHANGED_ACTION
WIFI_P2P_THIS_DEVICE_CHANGED_ACTION
In this section, we are going to learn how to use these new Wi-Fi Direct APIs with a sample application.
In order to use Wi-Fi Direct APIs, we need to set the minimum SDK version to API Level 14 or more in AndroidManifest.xml
. Furthermore, we need some permission to use Wi-Fi Direct APIs. The AndroidManifest.xml
file should be as follows:
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.chapter9" android:versionCode="1" android:versionName="1.0" > <uses-sdk android:minSdkVersion="14" android:targetSdkVersion="15" /> <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" /> <uses-permission android:name="android.permission.CHANGE_WIFI_STATE" /> <uses-permission android:name="android.permission.CHANGE_NETWORK_STATE" /> <uses-permission android:name="android.permission.INTERNET" /> <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" /> <application android:icon="@drawable/ic_launcher" android:label="@string/app_name" android:theme="@style/AppTheme" > <activity android:name=".Chapter9Activity" android:label="@string/title_activity_chapter9" > <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> </application> </manifest>
The first class that we need is a class that extends BroadcastReceiver
and handles the intents that we listed previously in the
onReceive()
method. The constructor of this class should be as follows:
package com.chapter9; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.net.NetworkInfo; import android.net.wifi.p2p.WifiP2pManager; import android.net.wifi.p2p.WifiP2pManager.Channel; import android.net.wifi.p2p.WifiP2pManager.PeerListListener; import android.widget.Toast; public class Chapter9WiFiDirectBroadcastReceiver extends BroadcastReceiver { private WifiP2pManager manager; private Channel channel; private Chapter9Activity activity; public Chapter9WiFiDirectBroadcastReceiver(WifiP2pManager manager, Channel channel, Chapter9Activity activity) { super(); this.manager = manager; this.channel = channel; this.activity = activity; } }
As you can see in this code, we passed the Channel
, WifiP2pManager
, and the Activity
classes to the constructor as parameters because we will need them later in the onReceive()
method. We need to override the
onReceive()
method of BroadcastReceiver
as shown in the following code block:
@Override public void onReceive(Context context, Intent intent) { String action = intent.getAction(); if (WifiP2pManager.WIFI_P2P_STATE_CHANGED_ACTION.equals(action)) { int state = intent.getIntExtra(WifiP2pManager.EXTRA_WIFI_STATE, -1); if (state == WifiP2pManager.WIFI_P2P_STATE_ENABLED) { // Wifi Direct mode is enabled Toast.makeText(activity, "wifi direct is enabled",Toast.LENGTH_LONG).show(); } else { // Wifi Direct mode is disabled Toast.makeText(activity, "wifi direct is disabled",Toast.LENGTH_LONG).show(); } } else if (WifiP2pManager.WIFI_P2P_PEERS_CHANGED_ACTION.equals(action)) { // request peers from the wifi p2p manager if (manager != null) { manager.requestPeers(channel, (PeerListListener) activity); } } else if (WifiP2pManager.WIFI_P2P_CONNECTION_CHANGED_ACTION.equals(action)) { if (manager == null) { return; } NetworkInfo networkInfo = (NetworkInfo) intent .getParcelableExtra(WifiP2pManager.EXTRA_NETWORK_INFO); if (networkInfo.isConnected()) { // request connection info manager.requestConnectionInfo(channel, activity); } else { // It's a disconnect } } else if (WifiP2pManager.WIFI_P2P_THIS_DEVICE_CHANGED_ACTION.equals(action)) { } }
In this method, we handle the received intents. Firstly, we check whether the intent is WIFI_P2P_STATE_CHANGED_ACTION
. This intent is received when Wi-Fi Direct is enabled or disabled. We receive the Wi-Fi Direct status from the intent and take action according to the Wi-Fi Direct status.
Secondly, we check whether the intent is
WIFI_P2P_PEERS_CHANGED_ACTION
. This intent is received when the
discoverPeers()
method of the WifiP2pManager
class is called. We get the list of the peers from the requestPeers()
method of the Wifi2P2pManager
class when we receive the WIFI_P2P_PEERS_CHANGED_ACTION
intent.
Next, we check whether the received intent is WIFI_P2P_CONNECTION_CHANGED_ACTION
. This intent is received when the Wi-Fi connection changes. We handle connections or disconnections when we receive the WIFI_P2P_CONNECTION_CHANGED_ACTION
intent. We firstly get NetworkInfo
from the intent to understand whether there is a connection or disconnection. If it is a connection, we call the
requestConnectionInfo()
method of WifiP2pManager
to connect.
Lastly, we check whether the intent is
WIFI_P2P_THIS_DEVICE_CHANGED_ACTION
. We receive this intent when the device details have changed. We do nothing for this intent.
We have a simple user interface for this application; a layout with two buttons. The first button is to find and second button is to connect peers. The XML code of the layout is as follows:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" > <Button android:id="@+id/buttonFind" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="find" /> <Button android:id="@+id/buttonConnect" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="connect" /> </LinearLayout>
The user interface will look like the following screenshot:
Lastly, we need to implement the Activity
class of this application. The code of the Activity
class should be as follows:
package com.chapter9; import android.app.Activity; import android.content.BroadcastReceiver; import android.content.Context; import android.content.IntentFilter; import android.net.wifi.p2p.WifiP2pConfig; import android.net.wifi.p2p.WifiP2pDevice; import android.net.wifi.p2p.WifiP2pDeviceList; import android.net.wifi.p2p.WifiP2pInfo; import android.net.wifi.p2p.WifiP2pManager; import android.net.wifi.p2p.WifiP2pManager.ActionListener; import android.net.wifi.p2p.WifiP2pManager.Channel; import android.net.wifi.p2p.WifiP2pManager.ChannelListener; import android.net.wifi.p2p.WifiP2pManager.ConnectionInfoListener; import android.net.wifi.p2p.WifiP2pManager.PeerListListener; import android.os.Bundle; import android.util.Log; import android.view.Menu; import android.view.View; import android.view.View.OnClickListener; import android.widget.Button; import android.widget.Toast; public class Chapter9Activity extends Activity implements ChannelListener,OnClickListener,PeerListListener,ConnectionInfoListener { private WifiP2pManager manager; private final IntentFilter intentFilter = new IntentFilter(); private Channel channel; private BroadcastReceiver receiver = null; private Button buttonFind; private Button buttonConnect; private WifiP2pDevice device; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); manager = (WifiP2pManager) getSystemService(Context.WIFI_P2P_SERVICE); channel = manager.initialize(this, getMainLooper(), null); intentFilter.addAction(WifiP2pManager.WIFI_P2P_STATE_CHANGED_ACTION); intentFilter.addAction(WifiP2pManager.WIFI_P2P_PEERS_CHANGED_ACTION); intentFilter.addAction(WifiP2pManager.WIFI_P2P_CONNECTION_CHANGED_ACTION); intentFilter.addAction(WifiP2pManager.WIFI_P2P_THIS_DEVICE_CHANGED_ACTION); receiver = new Chapter9WiFiDirectBroadcastReceiver(manager, channel, this); registerReceiver(receiver, intentFilter); this.buttonConnect = (Button) this.findViewById(R.id.buttonConnect); this.buttonConnect.setOnClickListener(this); this.buttonFind = (Button)this.findViewById(R.id.buttonFind); this.buttonFind.setOnClickListener(this); } }
The implementation is not complete currently. We will add the necessary methods step-by-step.
As you can see in this code, our Activity
class implements various Listeners
to handle the Wi-Fi Direct events. ConnectionInfoListener
is for the callback when the connection info is available.
PeerListListener
is for the callback when the peer list is available. ChannelListener
is for the callback when the channel is lost.
We create an intent filter and add the intents that we will check in the onReceive()
method of the class that extends BroadcastReceiver
.
We initialize the
WifiP2pManager
class by calling the
initialize()
method. This will register our application with the Wi-Fi network.
onChannelDisconnected()
method because we implemented ChannelListener
, as shown in the following code block:@Override public void onChannelDisconnected() { //handle the channel lost event }
onPeersAvailable()
method because we implemented PeerListListener
, as shown in the following code block:@Override public void onPeersAvailable(WifiP2pDeviceList peerList) { for (WifiP2pDevice device : peerList.getDeviceList()) { this.device = device; break; } }
We get the available peerList
in this method. We get the first device and break the for
loop. We need the device for connection.
onConnectionInfoAvailable()
method because we implemented ConnectionInfoListener
, as shown in the following code block:@Override public void onConnectionInfoAvailable(WifiP2pInfo info) { String infoname = info.groupOwnerAddress.toString(); }
This is the place where we get the connection info and connect and send data to the peer. For instance, an AsyncTask
that transfers a file could be executed here.
onClick()
method for the buttons:@Override public void onClick(View v) { if(v == buttonConnect) { connect(this.device); } else if(v == buttonFind) { find(); } }
The find()
and connect()
methods are as follows:
public void connect(WifiP2pDevice device) { WifiP2pConfig config = new WifiP2pConfig(); if(device != null) { config.deviceAddress = device.deviceAddress; manager.connect(channel, config, new ActionListener() { @Override public void onSuccess() { //success } @Override public void onFailure(int reason) { //fail } }); } else { Toast.makeText(Chapter9Activity.this, "Couldn't connect, device is not found", Toast.LENGTH_SHORT).show(); } } public void find() { manager.discoverPeers(channel, new WifiP2pManager.ActionListener() { @Override public void onSuccess() { Toast.makeText(Chapter9Activity.this, "Finding Peers", Toast.LENGTH_SHORT).show(); } @Override public void onFailure(int reasonCode) { Toast.makeText(Chapter9Activity.this, "Couldnt find peers ", Toast.LENGTH_SHORT).show(); } }); }
When the find button is clicked, we call the
discoverPeers()
method of WifiP2pManager
to discover the available peers. As you will remember, calling the
discoverPeers()
method will cause BroadcastReceiver
to receive the WIFI_P2P_PEERS_CHANGED_ACTION
intent. Then we will request the peer list in BroadcastReceiver
.
When the connect button is clicked, we call the
connect()
method of the WifiP2pManager
using the device info. This starts a peer-to-peer connection with the specified device.
The sample application to introduce the Wi-Fi Direct APIs is complete with these methods.
35.170.81.33