Scott Main | f5e0970 | 2013-08-22 17:19:17 -0700 | [diff] [blame] | 1 | page.title=Creating P2P Connections with Wi-Fi |
Alexander Lucas | 969c243 | 2012-06-13 10:57:22 -0700 | [diff] [blame] | 2 | |
| 3 | trainingnavtop=true |
Alexander Lucas | 969c243 | 2012-06-13 10:57:22 -0700 | [diff] [blame] | 4 | |
| 5 | @jd:body |
| 6 | |
| 7 | <div id="tb-wrapper"> |
| 8 | <div id="tb"> |
| 9 | <h2>This lesson teaches you how to</h2> |
| 10 | <ol> |
| 11 | <li><a href="#permissions">Set Up Application Permissions</a></li> |
| 12 | <li><a href="#receiver">Set Up the Broadcast Receiver and Peer-to-Peer |
| 13 | Manager</a></li> |
| 14 | <li><a href="#discover">Initiate Peer Discovery</a></li> |
| 15 | <li><a href="#fetch">Fetch the List of Peers</a></li> |
| 16 | <li><a href="#connect">Connect to a Peer</a></li> |
| 17 | </ol> |
Scott Main | f5e0970 | 2013-08-22 17:19:17 -0700 | [diff] [blame] | 18 | <h2>You should also read</h2> |
| 19 | <ul> |
| 20 | <li><a href="{@docRoot}guide/topics/connectivity/wifip2p.html">Wi-Fi Peer-to-Peer</a></li> |
| 21 | </ul> |
Alexander Lucas | 969c243 | 2012-06-13 10:57:22 -0700 | [diff] [blame] | 22 | </div> |
| 23 | </div> |
| 24 | |
Scott Main | f5e0970 | 2013-08-22 17:19:17 -0700 | [diff] [blame] | 25 | <p>The Wi-Fi peer-to-peer (P2P) APIs allow applications to connect to nearby devices without |
Scott Main | 2d006d2 | 2013-09-16 13:11:07 -0700 | [diff] [blame] | 26 | needing to connect to a network or hotspot (Android's Wi-Fi P2P framework complies with the |
| 27 | <a href="http://www.wi-fi.org/discover-and-learn/wi-fi-direct" |
| 28 | class="external-link">Wi-Fi Direct™</a> certification program). |
| 29 | Wi-Fi P2P allows your application to quickly |
Alexander Lucas | 969c243 | 2012-06-13 10:57:22 -0700 | [diff] [blame] | 30 | find and interact with nearby devices, at a range beyond the capabilities of Bluetooth. |
| 31 | </p> |
| 32 | <p> |
Scott Main | f5e0970 | 2013-08-22 17:19:17 -0700 | [diff] [blame] | 33 | This lesson shows you how to find and connect to nearby devices using Wi-Fi P2P. |
Alexander Lucas | 969c243 | 2012-06-13 10:57:22 -0700 | [diff] [blame] | 34 | </p> |
| 35 | <h2 id="permissions">Set Up Application Permissions</h2> |
Scott Main | f5e0970 | 2013-08-22 17:19:17 -0700 | [diff] [blame] | 36 | <p>In order to use Wi-Fi P2P, add the {@link |
Alexander Lucas | 969c243 | 2012-06-13 10:57:22 -0700 | [diff] [blame] | 37 | android.Manifest.permission#CHANGE_WIFI_STATE}, {@link |
| 38 | android.Manifest.permission#ACCESS_WIFI_STATE}, |
| 39 | and {@link android.Manifest.permission#INTERNET} |
Scott Main | f5e0970 | 2013-08-22 17:19:17 -0700 | [diff] [blame] | 40 | permissions to your manifest. Wi-Fi P2P doesn't require an internet connection, |
Alexander Lucas | 969c243 | 2012-06-13 10:57:22 -0700 | [diff] [blame] | 41 | but it does use standard Java sockets, which require the {@link |
| 42 | android.Manifest.permission#INTERNET} permission. |
Scott Main | f5e0970 | 2013-08-22 17:19:17 -0700 | [diff] [blame] | 43 | So you need the following permissions to use Wi-Fi P2P.</p> |
Alexander Lucas | 969c243 | 2012-06-13 10:57:22 -0700 | [diff] [blame] | 44 | |
| 45 | <pre> |
| 46 | <manifest xmlns:android="http://schemas.android.com/apk/res/android" |
| 47 | package="com.example.android.nsdchat" |
| 48 | ... |
| 49 | |
| 50 | <uses-permission |
| 51 | android:required="true" |
| 52 | android:name="android.permission.ACCESS_WIFI_STATE"/> |
| 53 | <uses-permission |
| 54 | android:required="true" |
| 55 | android:name="android.permission.CHANGE_WIFI_STATE"/> |
| 56 | <uses-permission |
| 57 | android:required="true" |
| 58 | android:name="android.permission.INTERNET"/> |
| 59 | ... |
| 60 | </pre> |
| 61 | |
| 62 | <h2 id="receiver">Set Up a Broadcast Receiver and Peer-to-Peer Manager</h2> |
Scott Main | f5e0970 | 2013-08-22 17:19:17 -0700 | [diff] [blame] | 63 | <p>To use Wi-Fi P2P, you need to listen for broadcast intents that tell your |
Alexander Lucas | 969c243 | 2012-06-13 10:57:22 -0700 | [diff] [blame] | 64 | application when certain events have occurred. In your application, instantiate |
| 65 | an {@link |
| 66 | android.content.IntentFilter} and set it to listen for the following:</p> |
| 67 | <dl> |
| 68 | <dt>{@link android.net.wifi.p2p.WifiP2pManager#WIFI_P2P_STATE_CHANGED_ACTION}</dt> |
Scott Main | f5e0970 | 2013-08-22 17:19:17 -0700 | [diff] [blame] | 69 | <dd>Indicates whether Wi-Fi P2P is enabled</dd> |
Alexander Lucas | 969c243 | 2012-06-13 10:57:22 -0700 | [diff] [blame] | 70 | <dt>{@link android.net.wifi.p2p.WifiP2pManager#WIFI_P2P_PEERS_CHANGED_ACTION}</dt> |
| 71 | <dd>Indicates that the available peer list has changed.</dd> |
| 72 | <dt>{@link android.net.wifi.p2p.WifiP2pManager#WIFI_P2P_CONNECTION_CHANGED_ACTION}</dt> |
| 73 | <dd>Indicates the state of Wi-Fi P2P connectivity has changed.</dd> |
| 74 | <dt>{@link android.net.wifi.p2p.WifiP2pManager#WIFI_P2P_THIS_DEVICE_CHANGED_ACTION}</dt> |
| 75 | <dd>Indicates this device's configuration details have changed.</dd> |
| 76 | <pre> |
| 77 | private final IntentFilter intentFilter = new IntentFilter(); |
| 78 | ... |
| 79 | @Override |
| 80 | public void onCreate(Bundle savedInstanceState) { |
| 81 | super.onCreate(savedInstanceState); |
| 82 | setContentView(R.layout.main); |
| 83 | |
Scott Main | f5e0970 | 2013-08-22 17:19:17 -0700 | [diff] [blame] | 84 | // Indicates a change in the Wi-Fi P2P status. |
Alexander Lucas | 969c243 | 2012-06-13 10:57:22 -0700 | [diff] [blame] | 85 | intentFilter.addAction(WifiP2pManager.WIFI_P2P_STATE_CHANGED_ACTION); |
| 86 | |
| 87 | // Indicates a change in the list of available peers. |
| 88 | intentFilter.addAction(WifiP2pManager.WIFI_P2P_PEERS_CHANGED_ACTION); |
| 89 | |
| 90 | // Indicates the state of Wi-Fi P2P connectivity has changed. |
| 91 | intentFilter.addAction(WifiP2pManager.WIFI_P2P_CONNECTION_CHANGED_ACTION); |
| 92 | |
| 93 | // Indicates this device's details have changed. |
| 94 | intentFilter.addAction(WifiP2pManager.WIFI_P2P_THIS_DEVICE_CHANGED_ACTION); |
| 95 | |
| 96 | ... |
| 97 | } |
| 98 | </pre> |
| 99 | |
| 100 | <p>At the end of the {@link android.app.Activity#onCreate onCreate()} method, get an instance of the {@link |
| 101 | android.net.wifi.p2p.WifiP2pManager}, and call its {@link |
| 102 | android.net.wifi.p2p.WifiP2pManager#initialize(Context, Looper, WifiP2pManager.ChannelListener) initialize()} |
| 103 | method. This method returns a {@link |
| 104 | android.net.wifi.p2p.WifiP2pManager.Channel} object, which you'll use later to |
Scott Main | f5e0970 | 2013-08-22 17:19:17 -0700 | [diff] [blame] | 105 | connect your app to the Wi-Fi P2P framework.</p> |
Alexander Lucas | 969c243 | 2012-06-13 10:57:22 -0700 | [diff] [blame] | 106 | |
| 107 | <pre> |
| 108 | @Override |
| 109 | |
| 110 | Channel mChannel; |
| 111 | |
| 112 | public void onCreate(Bundle savedInstanceState) { |
| 113 | .... |
| 114 | mManager = (WifiP2pManager) getSystemService(Context.WIFI_P2P_SERVICE); |
| 115 | mChannel = mManager.initialize(this, getMainLooper(), null); |
| 116 | } |
| 117 | </pre> |
| 118 | <p>Now create a new {@link |
| 119 | android.content.BroadcastReceiver} class that you'll use to listen for changes |
| 120 | to the System's Wi-Fi P2P state. In the {@link |
| 121 | android.content.BroadcastReceiver#onReceive(Context, Intent) onReceive()} |
| 122 | method, add a condition to handle each P2P state change listed above.</p> |
| 123 | |
| 124 | <pre> |
| 125 | |
| 126 | @Override |
| 127 | public void onReceive(Context context, Intent intent) { |
| 128 | String action = intent.getAction(); |
| 129 | if (WifiP2pManager.WIFI_P2P_STATE_CHANGED_ACTION.equals(action)) { |
Scott Main | f5e0970 | 2013-08-22 17:19:17 -0700 | [diff] [blame] | 130 | // Determine if Wifi P2P mode is enabled or not, alert |
Alexander Lucas | 969c243 | 2012-06-13 10:57:22 -0700 | [diff] [blame] | 131 | // the Activity. |
| 132 | int state = intent.getIntExtra(WifiP2pManager.EXTRA_WIFI_STATE, -1); |
| 133 | if (state == WifiP2pManager.WIFI_P2P_STATE_ENABLED) { |
| 134 | activity.setIsWifiP2pEnabled(true); |
| 135 | } else { |
| 136 | activity.setIsWifiP2pEnabled(false); |
| 137 | } |
| 138 | } else if (WifiP2pManager.WIFI_P2P_PEERS_CHANGED_ACTION.equals(action)) { |
| 139 | |
| 140 | // The peer list has changed! We should probably do something about |
| 141 | // that. |
| 142 | |
| 143 | } else if (WifiP2pManager.WIFI_P2P_CONNECTION_CHANGED_ACTION.equals(action)) { |
| 144 | |
| 145 | // Connection state changed! We should probably do something about |
| 146 | // that. |
| 147 | |
| 148 | } else if (WifiP2pManager.WIFI_P2P_THIS_DEVICE_CHANGED_ACTION.equals(action)) { |
| 149 | DeviceListFragment fragment = (DeviceListFragment) activity.getFragmentManager() |
| 150 | .findFragmentById(R.id.frag_list); |
| 151 | fragment.updateThisDevice((WifiP2pDevice) intent.getParcelableExtra( |
| 152 | WifiP2pManager.EXTRA_WIFI_P2P_DEVICE)); |
| 153 | |
| 154 | } |
| 155 | } |
| 156 | </pre> |
| 157 | |
| 158 | <p>Finally, add code to register the intent filter and broadcast receiver when |
| 159 | your main activity is active, and unregister them when the activity is paused. |
| 160 | The best place to do this is the {@link android.app.Activity#onResume()} and |
| 161 | {@link android.app.Activity#onPause()} methods. |
| 162 | |
| 163 | <pre> |
| 164 | /** register the BroadcastReceiver with the intent values to be matched */ |
| 165 | @Override |
| 166 | public void onResume() { |
| 167 | super.onResume(); |
| 168 | receiver = new WiFiDirectBroadcastReceiver(mManager, mChannel, this); |
| 169 | registerReceiver(receiver, intentFilter); |
| 170 | } |
| 171 | |
| 172 | @Override |
| 173 | public void onPause() { |
| 174 | super.onPause(); |
| 175 | unregisterReceiver(receiver); |
| 176 | } |
| 177 | </pre> |
| 178 | |
| 179 | |
| 180 | <h2 id="discover">Initiate Peer Discovery</h2> |
Scott Main | f5e0970 | 2013-08-22 17:19:17 -0700 | [diff] [blame] | 181 | <p>To start searching for nearby devices with Wi-Fi P2P, call {@link |
Alexander Lucas | 969c243 | 2012-06-13 10:57:22 -0700 | [diff] [blame] | 182 | android.net.wifi.p2p.WifiP2pManager#discoverPeers(WifiP2pManager.Channel, |
| 183 | WifiP2pManager.ActionListener) discoverPeers()}. This method takes the |
| 184 | following arguments:</p> |
| 185 | <ul> |
| 186 | <li>The {@link android.net.wifi.p2p.WifiP2pManager.Channel} you |
| 187 | received back when you initialized the peer-to-peer mManager</li> |
| 188 | <li>An implementation of {@link android.net.wifi.p2p.WifiP2pManager.ActionListener} with methods |
| 189 | the system invokes for successful and unsuccessful discovery.</li> |
| 190 | </ul> |
| 191 | |
| 192 | <pre> |
| 193 | mManager.discoverPeers(mChannel, new WifiP2pManager.ActionListener() { |
| 194 | |
| 195 | @Override |
| 196 | public void onSuccess() { |
| 197 | // Code for when the discovery initiation is successful goes here. |
| 198 | // No services have actually been discovered yet, so this method |
| 199 | // can often be left blank. Code for peer discovery goes in the |
| 200 | // onReceive method, detailed below. |
| 201 | } |
| 202 | |
| 203 | @Override |
| 204 | public void onFailure(int reasonCode) { |
| 205 | // Code for when the discovery initiation fails goes here. |
| 206 | // Alert the user that something went wrong. |
| 207 | } |
| 208 | }); |
| 209 | </pre> |
| 210 | |
| 211 | <p>Keep in mind that this only <em>initiates</em> peer discovery. The |
| 212 | {@link android.net.wifi.p2p.WifiP2pManager#discoverPeers(WifiP2pManager.Channel, |
| 213 | WifiP2pManager.ActionListener) discoverPeers()} method starts the discovery process and then |
| 214 | immediately returns. The system notifies you if the peer discovery process is |
| 215 | successfully initiated by calling methods in the provided action listener. |
| 216 | Also, discovery will remain active until a connection is initiated or a P2P group is |
| 217 | formed.</p> |
| 218 | |
| 219 | <h2 id="fetch">Fetch the List of Peers</h2> |
| 220 | <p>Now write the code that fetches and processes the list of peers. First |
| 221 | implement the {@link android.net.wifi.p2p.WifiP2pManager.PeerListListener} |
Scott Main | f5e0970 | 2013-08-22 17:19:17 -0700 | [diff] [blame] | 222 | interface, which provides information about the peers that Wi-Fi P2P has |
Alexander Lucas | 969c243 | 2012-06-13 10:57:22 -0700 | [diff] [blame] | 223 | detected. The following code snippet illustrates this.</p> |
| 224 | |
| 225 | <pre> |
| 226 | private List<WifiP2pDevice> peers = new ArrayList<WifiP2pDevice>(); |
| 227 | ... |
| 228 | |
| 229 | private PeerListListener peerListListener = new PeerListListener() { |
| 230 | @Override |
| 231 | public void onPeersAvailable(WifiP2pDeviceList peerList) { |
| 232 | |
| 233 | // Out with the old, in with the new. |
| 234 | peers.clear(); |
| 235 | peers.addAll(peerList.getDeviceList()); |
| 236 | |
| 237 | // If an AdapterView is backed by this data, notify it |
| 238 | // of the change. For instance, if you have a ListView of available |
| 239 | // peers, trigger an update. |
| 240 | ((WiFiPeerListAdapter) getListAdapter()).notifyDataSetChanged(); |
| 241 | if (peers.size() == 0) { |
| 242 | Log.d(WiFiDirectActivity.TAG, "No devices found"); |
| 243 | return; |
| 244 | } |
| 245 | } |
| 246 | } |
| 247 | </pre> |
| 248 | |
| 249 | <p>Now modify your broadcast receiver's {@link |
| 250 | android.content.BroadcastReceiver#onReceive(Context, Intent) onReceive()} |
| 251 | method to call {@link android.net.wifi.p2p.WifiP2pManager#requestPeers |
| 252 | requestPeers()} when an intent with the action {@link |
| 253 | android.net.wifi.p2p.WifiP2pManager#WIFI_P2P_PEERS_CHANGED_ACTION} is received. You |
| 254 | need to pass this listener into the receiver somehow. One way is to send it |
| 255 | as an argument to the broadcast receiver's constructor. |
| 256 | </p> |
| 257 | |
| 258 | <pre> |
| 259 | public void onReceive(Context context, Intent intent) { |
| 260 | ... |
| 261 | else if (WifiP2pManager.WIFI_P2P_PEERS_CHANGED_ACTION.equals(action)) { |
| 262 | |
| 263 | // Request available peers from the wifi p2p manager. This is an |
| 264 | // asynchronous call and the calling activity is notified with a |
| 265 | // callback on PeerListListener.onPeersAvailable() |
| 266 | if (mManager != null) { |
Scott Main | f8daf19 | 2013-01-04 17:41:54 -0800 | [diff] [blame] | 267 | mManager.requestPeers(mChannel, peerListListener); |
Alexander Lucas | 969c243 | 2012-06-13 10:57:22 -0700 | [diff] [blame] | 268 | } |
| 269 | Log.d(WiFiDirectActivity.TAG, "P2P peers changed"); |
| 270 | }... |
| 271 | } |
| 272 | </pre> |
| 273 | |
| 274 | <p>Now, an intent with the action {@link |
| 275 | android.net.wifi.p2p.WifiP2pManager#WIFI_P2P_PEERS_CHANGED_ACTION} intent will |
| 276 | trigger a request for an updated peer list. </p> |
| 277 | |
| 278 | <h2 id="connect">Connect to a Peer</h2> |
| 279 | <p>In order to connect to a peer, create a new {@link |
| 280 | android.net.wifi.p2p.WifiP2pConfig} object, and copy data into it from the |
| 281 | {@link android.net.wifi.p2p.WifiP2pDevice} representing the device you want to |
| 282 | connect to. Then call the {@link |
| 283 | android.net.wifi.p2p.WifiP2pManager#connect(WifiP2pManager.Channel, |
| 284 | WifiP2pConfig, WifiP2pManager.ActionListener) connect()} |
| 285 | method.</p> |
| 286 | |
| 287 | <pre> |
| 288 | @Override |
| 289 | public void connect() { |
| 290 | // Picking the first device found on the network. |
| 291 | WifiP2pDevice device = peers.get(0); |
| 292 | |
| 293 | WifiP2pConfig config = new WifiP2pConfig(); |
| 294 | config.deviceAddress = device.deviceAddress; |
| 295 | config.wps.setup = WpsInfo.PBC; |
| 296 | |
| 297 | mManager.connect(mChannel, config, new ActionListener() { |
| 298 | |
| 299 | @Override |
| 300 | public void onSuccess() { |
| 301 | // WiFiDirectBroadcastReceiver will notify us. Ignore for now. |
| 302 | } |
| 303 | |
| 304 | @Override |
| 305 | public void onFailure(int reason) { |
| 306 | Toast.makeText(WiFiDirectActivity.this, "Connect failed. Retry.", |
| 307 | Toast.LENGTH_SHORT).show(); |
| 308 | } |
| 309 | }); |
| 310 | } |
| 311 | </pre> |
| 312 | |
| 313 | <p>The {@link android.net.wifi.p2p.WifiP2pManager.ActionListener} implemented in |
| 314 | this snippet only notifies you when the <em>initiation</em> succeeds or fails. |
| 315 | To listen for <em>changes</em> in connection state, implement the {@link |
| 316 | android.net.wifi.p2p.WifiP2pManager.ConnectionInfoListener} interface. Its {@link |
| 317 | android.net.wifi.p2p.WifiP2pManager.ConnectionInfoListener#onConnectionInfoAvailable(WifiP2pInfo) |
| 318 | onConnectionInfoAvailable()} |
| 319 | callback will notify you when the state of the connection changes. In cases |
| 320 | where multiple devices are going to be connected to a single device (like a game with |
| 321 | 3 or more players, or a chat app), one device will be designated the "group |
| 322 | owner".</p> |
| 323 | |
| 324 | <pre> |
| 325 | @Override |
| 326 | public void onConnectionInfoAvailable(final WifiP2pInfo info) { |
| 327 | |
| 328 | // InetAddress from WifiP2pInfo struct. |
| 329 | InetAddress groupOwnerAddress = info.groupOwnerAddress.getHostAddress()); |
| 330 | |
| 331 | // After the group negotiation, we can determine the group owner. |
| 332 | if (info.groupFormed && info.isGroupOwner) { |
| 333 | // Do whatever tasks are specific to the group owner. |
| 334 | // One common case is creating a server thread and accepting |
| 335 | // incoming connections. |
| 336 | } else if (info.groupFormed) { |
| 337 | // The other device acts as the client. In this case, |
| 338 | // you'll want to create a client thread that connects to the group |
| 339 | // owner. |
| 340 | } |
| 341 | } |
| 342 | </pre> |
| 343 | |
| 344 | <p>Now go back to the {@link |
| 345 | android.content.BroadcastReceiver#onReceive(Context, Intent) onReceive()} method of the broadcast receiver, and modify the section |
| 346 | that listens for a {@link |
| 347 | android.net.wifi.p2p.WifiP2pManager#WIFI_P2P_CONNECTION_CHANGED_ACTION} intent. |
| 348 | When this intent is received, call {@link |
| 349 | android.net.wifi.p2p.WifiP2pManager#requestConnectionInfo(WifiP2pManager.Channel, |
| 350 | WifiP2pManager.ConnectionInfoListener) requestConnectionInfo()}. This is an |
| 351 | asynchronous call, so results will be received by the connection info listener |
| 352 | you provide as a parameter. |
| 353 | |
| 354 | <pre> |
| 355 | ... |
| 356 | } else if (WifiP2pManager.WIFI_P2P_CONNECTION_CHANGED_ACTION.equals(action)) { |
| 357 | |
| 358 | if (mManager == null) { |
| 359 | return; |
| 360 | } |
| 361 | |
| 362 | NetworkInfo networkInfo = (NetworkInfo) intent |
| 363 | .getParcelableExtra(WifiP2pManager.EXTRA_NETWORK_INFO); |
| 364 | |
| 365 | if (networkInfo.isConnected()) { |
| 366 | |
| 367 | // We are connected with the other device, request connection |
| 368 | // info to find group owner IP |
| 369 | |
| 370 | mManager.requestConnectionInfo(mChannel, connectionListener); |
| 371 | } |
| 372 | ... |
| 373 | </pre> |