diff options
| -rw-r--r-- | docs/html/shareables/training/nsdchat.zip | bin | 0 -> 46385 bytes | |||
| -rw-r--r-- | docs/html/training/connect-devices-wirelessly/index.jd | 62 | ||||
| -rw-r--r-- | docs/html/training/connect-devices-wirelessly/nsd-wifi-direct.jd | 252 | ||||
| -rw-r--r-- | docs/html/training/connect-devices-wirelessly/nsd.jd | 373 | ||||
| -rw-r--r-- | docs/html/training/connect-devices-wirelessly/wifi-direct.jd | 372 | ||||
| -rw-r--r-- | docs/html/training/training_toc.cs | 21 |
6 files changed, 1080 insertions, 0 deletions
diff --git a/docs/html/shareables/training/nsdchat.zip b/docs/html/shareables/training/nsdchat.zip Binary files differnew file mode 100644 index 000000000000..a10697570f9c --- /dev/null +++ b/docs/html/shareables/training/nsdchat.zip diff --git a/docs/html/training/connect-devices-wirelessly/index.jd b/docs/html/training/connect-devices-wirelessly/index.jd new file mode 100644 index 000000000000..37cf633a4538 --- /dev/null +++ b/docs/html/training/connect-devices-wirelessly/index.jd @@ -0,0 +1,62 @@ +page.title=Connecting Devices Wirelessly + +trainingnavtop=true +startpage=true +next.title=Using Network Service Discovery +next.link=nsd.html + +@jd:body + + +<div id="tb-wrapper"> +<div id="tb"> + +<h2>Dependencies and prerequisites</h2> +<ul> + <li>Android 4.1 or higher</li> +</ul> + +<h2>You should also read</h2> +<ul> + <li><a href="{@docRoot}guide/topics/connectivity/wifip2p.html">Wi-Fi Direct</a></li> +</ul> + + +</div> +</div> + + +<p>Besides enabling communication with the cloud, Android's wireless APIs also +enable communication with other devices on the same local network, and even +devices which are not on a network, but are physically nearby. The addition of +Network Service Discovery (NSD) takes this further by allowing an application to +seek out a nearby device running services with which it can communicate. +Integrating this functionality into your application helps you provide a wide range +of features, such as playing games with users in the same room, pulling +images from a networked NSD-enabled webcam, or remotely logging into +other machines on the same network.</p> +<p>This class describes the key APIs for finding and +connecting to other devices from your application. Specifically, it +describes the NSD API for discovering available services and the Wi-Fi +Direct™ API for doing peer-to-peer wireless connections. This class also +shows you how to use NSD and Wi-Fi Direct in +combination to detect the services offered by a device and connect to the +device when neither device is connected to a network. +</p> +<h2>Lessons</h2> + +<dl> + <dt><strong><a href="nsd.html">Using Network Service Discovery</a></strong></dt> + <dd>Learn how to broadcast services offered by your own application, discover + services offered on the local network, and use NSD to determine the connection + details for the service you wish to connect to.</dd> + <dt><strong><a href="wifi-direct.html">Connecting with Wi-Fi Direct</a></strong></dt> + <dd>Learn how to fetch a list of nearby peer devices, create an access point + for legacy devices, and connect to other devices capable of Wi-Fi Direct + connections.</dd> + <dt><strong><a href="nsd-wifi-direct.html">Using Wi-Fi Direct for Service + Discovery</a></strong></dt> + <dd>Learn how to discover services published by nearby devices without being + on the same network, using Wi-Fi Direct.</dd> +</dl> + diff --git a/docs/html/training/connect-devices-wirelessly/nsd-wifi-direct.jd b/docs/html/training/connect-devices-wirelessly/nsd-wifi-direct.jd new file mode 100644 index 000000000000..5e276ded5491 --- /dev/null +++ b/docs/html/training/connect-devices-wirelessly/nsd-wifi-direct.jd @@ -0,0 +1,252 @@ +page.title=Using Wi-Fi Direct for Service Discovery +parent.title=Connecting Devices Wirelessly +parent.link=index.html + +trainingnavtop=true + +@jd:body + +<div id="tb-wrapper"> + <div id="tb"> + <h2>This lesson teaches you to</h2> + <ol> + <li><a href="#manifest">Set Up the Manifest</a></li> + <li><a href="#register">Add a Local Service</a></li> + <li><a href="#discover">Discover Nearby Services</a></li> + </ol> + <!-- + <h2>You should also read</h2> + <ul> + <li><a href="#"></a></li> + </ul> + --> + </div> +</div> + +<p>The first lesson in this class, <a href="nsd.html">Using Network Service + Discovery</a>, showed you +how to discover services that are connected to a local network. However, using +Wi-Fi Direct&trad; Service Discovery allows you to discover the services of nearby devices directly, +without being connected to a network. You can also advertise the services +running on your device. These capabilities help you communicate between apps, +even when no local network or hotspot is available.</p> +<p>While this set of APIs is similar in purpose to the Network Service Discovery +APIs outlined in a previous lesson, implementing them in code is very different. +This lesson shows you how to discover services available from other devices, +using Wi-Fi Direct™. The lesson assumes that you're already familiar with the +<a href="{@docRoot}guide/topics/connectivity/wifip2p.html">Wi-Fi Direct</a> API.</p> + + +<h2 id="manifest">Set Up the Manifest</h2> +<p>In order to use Wi-Fi Direct, add the {@link +android.Manifest.permission#CHANGE_WIFI_STATE}, {@link +android.Manifest.permission#ACCESS_WIFI_STATE}, +and {@link android.Manifest.permission#INTERNET} +permissions to your manifest. Even though Wi-Fi Direct doesn't require an +Internet connection, it uses standard Java sockets, and using these in Android +requires the requested permissions.</p> + +<pre> +<manifest xmlns:android="http://schemas.android.com/apk/res/android" + package="com.example.android.nsdchat" + ... + + <uses-permission + android:required="true" + android:name="android.permission.ACCESS_WIFI_STATE"/> + <uses-permission + android:required="true" + android:name="android.permission.CHANGE_WIFI_STATE"/> + <uses-permission + android:required="true" + android:name="android.permission.INTERNET"/> + ... +</pre> + +<h2 id="register">Add a Local Service</h2> +<p>If you're providing a local service, you need to register it for +service discovery. Once your local service is registered, the framework +automatically responds to service discovery requests from peers.</p> + +<p>To create a local service:</p> + +<ol> + <li>Create a +{@link android.net.wifi.p2p.nsd.WifiP2pServiceInfo} object.</li> + <li>Populate it with information about your service.</li> + <li>Call {@link +android.net.wifi.p2p.WifiP2pManager#addLocalService(WifiP2pManager.Channel, +WifiP2pServiceInfo, WifiP2pManager.ActionListener) addLocalService()} to register the local +service for service discovery.</li> +</ol> + +<pre> + private void startRegistration() { + // Create a string map containing information about your service. + Map<String,String> record = new HashMap<String,String>(); + record.put("listenport", String.valueOf(SERVER_PORT)); + record.put("buddyname", "John Doe" + (int) (Math.random() * 1000)); + record.put("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 serviceInfo = + WifiP2pDnsSdServiceInfo.newInstance("_test", "_presence._tcp", record); + + // Add the local service, sending the service info, network channel, + // and listener that will be used to indicate success or failure of + // the request. + mManager.addLocalService(channel, serviceInfo, new ActionListener() { + @Override + public void onSuccess() { + // Command successful! Code isn't necessarily needed here, + // Unless you want to update the UI or add logging statements. + } + + @Override + public void onFailure(int arg0) { + // Command failed. Check for P2P_UNSUPPORTED, ERROR, or BUSY + } + }); + } +</pre> + +<h2 id="discover">Discover Nearby Services</h2> +<p>Android uses callback methods to notify your application of available services, so +the first thing to do is set those up. Create a {@link +android.net.wifi.p2p.WifiP2pManager.DnsSdTxtRecordListener} to listen for +incoming records. This record can optionally be broadcast by other +devices. When one comes in, copy the device address and any other +relevant information you want into a data structure external to the current +method, so you can access it later. The following example assumes that the +record contains a "buddyname" field, populated with the user's identity.</p> + +<pre> +final HashMap<String, String> buddies = new HashMap<String, String>(); +... +private void discoverService() { + DnsSdTxtRecordListener txtListener = new DnsSdTxtRecordListener() { + @Override + /* Callback includes: + * fullDomain: full domain name: e.g "printer._ipp._tcp.local." + * record: TXT record dta as a map of key/value pairs. + * device: The device running the advertised service. + */ + + public void onDnsSdTxtRecordAvailable( + String fullDomain, Map<String,String> record, WifiP2pDevice device) { + Log.d(TAG, "DnsSdTxtRecord available -" + record.toString()); + buddies.put(device.deviceAddress, record.get("buddyname")); + } + }; + ... +} +</pre> + +<p>To get the service information, create a {@link +android.net.wifi.p2p.WifiP2pManager.DnsSdServiceResponseListener}. This +receives the actual description and connection information. The previous code +snippet implemented a {@link java.util.Map} object to pair a device address with the buddy +name. The service response listener uses this to link the DNS record with the +corresponding service information. Once both +listeners are implemented, add them to the {@link +android.net.wifi.p2p.WifiP2pManager} using the {@link +android.net.wifi.p2p.WifiP2pManager#setDnsSdResponseListeners(WifiP2pManager.Channel, +WifiP2pManager.DnsSdServiceResponseListener, +WifiP2pManager.DnsSdTxtRecordListener) setDnsSdResponseListeners()} method.</p> + +<pre> +private void discoverService() { +... + + DnsSdServiceResponseListener servListener = new DnsSdServiceResponseListener() { + @Override + public void onDnsSdServiceAvailable(String instanceName, String registrationType, + WifiP2pDevice resourceType) { + + // Update the device name with the human-friendly version from + // the DnsTxtRecord, assuming one arrived. + resourceType.deviceName = buddies + .containsKey(resourceType.deviceAddress) ? buddies + .get(resourceType.deviceAddress) : resourceType.deviceName; + + // Add to the custom adapter defined specifically for showing + // wifi devices. + WiFiDirectServicesList fragment = (WiFiDirectServicesList) getFragmentManager() + .findFragmentById(R.id.frag_peerlist); + WiFiDevicesAdapter adapter = ((WiFiDevicesAdapter) fragment + .getListAdapter()); + + adapter.add(resourceType); + adapter.notifyDataSetChanged(); + Log.d(TAG, "onBonjourServiceAvailable " + instanceName); + } + }; + + mManager.setDnsSdResponseListeners(channel, servListener, txtListener); + ... +} +</pre> + +<p>Now create a service request and call {@link +android.net.wifi.p2p.WifiP2pManager#addServiceRequest(WifiP2pManager.Channel, +WifiP2pServiceRequest, WifiP2pManager.ActionListener) addServiceRequest()}. +This method also takes a listener to report success or failure.</p> + +<pre> + serviceRequest = WifiP2pDnsSdServiceRequest.newInstance(); + mManager.addServiceRequest(channel, + serviceRequest, + new ActionListener() { + @Override + public void onSuccess() { + // Success! + } + + @Override + public void onFailure(int code) { + // Command failed. Check for P2P_UNSUPPORTED, ERROR, or BUSY + } + }); +</pre> + +<p>Finally, make the call to {@link +android.net.wifi.p2p.WifiP2pManager#discoverServices(WifiP2pManager.Channel, +WifiP2pManager.ActionListener) discoverServices()}.</p> + +<pre> + mManager.discoverServices(channel, new ActionListener() { + + @Override + public void onSuccess() { + // Success! + } + + @Override + public void onFailure(int code) { + // Command failed. Check for P2P_UNSUPPORTED, ERROR, or BUSY + if (code == WifiP2pManager.P2P_UNSUPPORTED) { + Log.d(TAG, "P2P isn't supported on this device."); + else if(...) + ... + } + }); +</pre> + +<p>If all goes well, hooray, you're done! If you encounter problems, remember +that the asynchronous calls you've made take an +{@link android.net.wifi.p2p.WifiP2pManager.ActionListener} as an argument, and +this provides you with callbacks indicating success or failure. To diagnose +problems, put debugging code in {@link +android.net.wifi.p2p.WifiP2pManager.ActionListener#onFailure(int) onFailure()}. The error code +provided by the method hints at the problem. Here are the possible error values +and what they mean</p> +<dl> + <dt> {@link android.net.wifi.p2p.WifiP2pManager#P2P_UNSUPPORTED}</dt> + <dd> Wi-Fi Direct isn't supported on the device running the app.</dd> + <dt> {@link android.net.wifi.p2p.WifiP2pManager#BUSY}</dt> + <dd> The system is to busy to process the request.</dd> + <dt> {@link android.net.wifi.p2p.WifiP2pManager#ERROR}</dt> + <dd> The operation failed due to an internal error.</dd> +</dl> diff --git a/docs/html/training/connect-devices-wirelessly/nsd.jd b/docs/html/training/connect-devices-wirelessly/nsd.jd new file mode 100644 index 000000000000..d2b01a177a9f --- /dev/null +++ b/docs/html/training/connect-devices-wirelessly/nsd.jd @@ -0,0 +1,373 @@ +page.title=Using Network Service Discovery +parent.title=Connecting Devices Wirelessly +parent.link=index.html + +trainingnavtop=true +next.title=Connecting with Wi-Fi Direct +next.link=wifi-direct.html + +@jd:body + +<div id="tb-wrapper"> +<div id="tb"> + +<!-- table of contents --> +<h2>This lesson teaches you how to</h2> +<ol> + <li><a href="#register">Register Your Service on the Network</a></li> + <li><a href="#discover">Discover Services on the Network</a></li> + <li><a href="#connect">Connect to Services on the Network</a></li> + <li><a href="#teardown">Unregister Your Service on Application Close</a></li> +</ol> + +<!-- +<h2>You should also read</h2> + <ul> + </ul> +--> +<h2>Try it out</h2> + +<div class="download-box"> + <a href="{@docRoot}shareables/training/nsdchat.zip" class="button">Download + the sample app</a> + <p class="filename">nsdchat.zip</p> +</div> +</p> + +</div> +</div> + +<p>Adding Network Service Discovery (NSD) to your app allows your users to +identify other devices on the local network that support the services your app +requests. This is useful for a variety of peer-to-peer applications such as file +sharing or multi-player gaming. Android's NSD APIs simplify the effort required +for you to implement such features.</p> + +<p>This lesson shows you how to build an application that can broadcast its +name and connection information to the local network and scan for information +from other applications doing the same. Finally, this lesson shows you how +to connect to the same application running on another device.</p> + +<h2 id="register">Register Your Service on the Network</h2> + +<p class="note"><strong>Note: </strong>This step is optional. If +you don't care about broadcasting your app's services over the local network, +you can skip forward to the +next section, <a href="#discover">Discover Services on the Network</a>.</p> + +<p>To register your service on the local network, first create a {@link +android.net.nsd.NsdServiceInfo} object. This object provides the information +that other devices on the network use when they're deciding whether to connect to your +service. </p> + +<pre> +public void registerService(int port) { + // Create the NsdServiceInfo object, and populate it. + NsdServiceInfo serviceInfo = new NsdServiceInfo(); + + // The name is subject to change based on conflicts + // with other services advertised on the same network. + serviceInfo.setServiceName("NsdChat"); + serviceInfo.setServiceType("_http._tcp."); + serviceInfo.setPort(port); + .... +} +</pre> + +<p>This code snippet sets the service name to "NsdChat". +The name is visible to any device on the network that is using NSD to look for +local services. Keep in mind that the name must be unique for any service on the +network, and Android automatically handles conflict resolution. If +two devices on the network both have the NsdChat application installed, one of +them changes the service name automatically, to something like "NsdChat +(1)".</p> + +<p>The second parameter sets the service type, specifies which protocol and transport +layer the application uses. The syntax is +"_<protocol>._<transportlayer>". In the +code snippet, the service uses HTTP protocol running over TCP. An application +offering a printer service (for instance, a network printer) would set the +service type to "_ipp._tcp".</p> + +<p class="note"><strong>Note: </strong> The International Assigned Numbers +Authority (IANA) manages a centralized, +authoritative list of service types used by service discovery protocols such as NSD and Bonjour. +You can download the list from <a + href="http://www.iana.org/assignments/service-names-port-numbers/service-names-port-numbers.xml">the +IANA list of service names and port numbers</a>. +If you intend to use a new service type, you should reserve it by filling out +the <a + href="http://www.iana.org/form/ports-services">IANA Ports and Service + registration form</a>.</p> + +<p>When setting the port for your service, avoid hardcoding it as this +conflicts with other applications. For instance, assuming +that your application always uses port 1337 puts it in potential conflict with +other installed applications that use the same port. Instead, use the device's +next available port. Because this information is provided to other apps by a +service broadcast, there's no need for the port your application uses to be +known by other applications at compile-time. Instead, the applications can get +this information from your service broadcast, right before connecting to your +service.</p> + +<p>If you're working with sockets, here's how you can initialize a socket to any +available port simply by setting it to 0.</p> + +<pre> +public void initializeServerSocket() { + // Initialize a server socket on the next available port. + mServerSocket = new ServerSocket(0); + + // Store the chosen port. + mLocalPort = mServerSocket.getLocalPort(); + ... +} +</pre> + +<p>Now that you've defined the {@link android.net.nsd.NsdServiceInfo +NsdServiceInfo} object, you need to implement the {@link +android.net.nsd.NsdManager.RegistrationListener RegistrationListener} interface. This +interface contains callbacks used by Android to alert your application of the +success or failure of service registration and unregistration. +</p> +<pre> +public void initializeRegistrationListener() { + mRegistrationListener = new NsdManager.RegistrationListener() { + + @Override + public void onServiceRegistered(NsdServiceInfo NsdServiceInfo) { + // Save the service name. Android may have changed it in order to + // resolve a conflict, so update the name you initially requested + // with the name Android actually used. + mServiceName = NsdServiceInfo.getServiceName(); + } + + @Override + public void onRegistrationFailed(NsdServiceInfo serviceInfo, int errorCode) { + // Registration failed! Put debugging code here to determine why. + } + + @Override + public void onServiceUnregistered(NsdServiceInfo arg0) { + // Service has been unregistered. This only happens when you call + // NsdManager.unregisterService() and pass in this listener. + } + + @Override + public void onUnregistrationFailed(NsdServiceInfo serviceInfo, int errorCode) { + // Unregistration failed. Put debugging code here to determine why. + } + }; +} +</pre> + +<p>Now you have all the pieces to register your service. Call the method +{@link android.net.nsd.NsdManager#registerService registerService()}. +</p> + +<p>Note that this method is asynchronous, so any code that needs to run +after the service has been registered must go in the {@link +android.net.nsd.NsdManager.RegistrationListener#onServiceRegistered(NsdServiceInfo) +onServiceRegistered()} method.</p> + +<pre> +public void registerService(int port) { + NsdServiceInfo serviceInfo = new NsdServiceInfo(); + serviceInfo.setServiceName("NsdChat"); + serviceInfo.setServiceType("_http._tcp."); + serviceInfo.setPort(port); + + mNsdManager = Context.getSystemService(Context.NSD_SERVICE); + + mNsdManager.registerService( + serviceInfo, NsdManager.PROTOCOL_DNS_SD, mRegistrationListener); +} +</pre> + +<h2 id="discover">Discover Services on the Network</h2> +<p>The network is teeming with life, from the beastly network printers to the +docile network webcams, to the brutal, fiery battles of nearby tic-tac-toe +players. The key to letting your application see this vibrant ecosystem of +functionality is service discovery. Your application needs to listen to service +broadcasts on the network to see what services are available, and filter out +anything the application can't work with.</p> + +<p>Service discovery, like service registration, has two steps: + setting up a discovery listener with the relevant callbacks, and making a single asynchronous +API call to {@link android.net.nsd.NsdManager#discoverServices(String +, int , NsdManager.DiscoveryListener) discoverServices()}.</p> + +<p>First, instantiate an anonymous class that implements {@link +android.net.nsd.NsdManager.DiscoveryListener}. The following snippet shows a +simple example:</p> + +<pre> +public void initializeDiscoveryListener() { + + // Instantiate a new DiscoveryListener + mDiscoveryListener = new NsdManager.DiscoveryListener() { + + // Called as soon as service discovery begins. + @Override + public void onDiscoveryStarted(String regType) { + Log.d(TAG, "Service discovery started"); + } + + @Override + public void onServiceFound(NsdServiceInfo service) { + // A service was found! Do something with it. + Log.d(TAG, "Service discovery success" + service); + if (!service.getServiceType().equals(SERVICE_TYPE)) { + // Service type is the string containing the protocol and + // transport layer for this service. + Log.d(TAG, "Unknown Service Type: " + service.getServiceType()); + } else if (service.getServiceName().equals(mServiceName)) { + // The name of the service tells the user what they'd be + // connecting to. It could be "Bob's Chat App". + Log.d(TAG, "Same machine: " + mServiceName); + } else if (service.getServiceName().contains("NsdChat")){ + mNsdManager.resolveService(service, mResolveListener); + } + } + + @Override + public void onServiceLost(NsdServiceInfo service) { + // When the network service is no longer available. + // Internal bookkeeping code goes here. + Log.e(TAG, "service lost" + service); + } + + @Override + public void onDiscoveryStopped(String serviceType) { + Log.i(TAG, "Discovery stopped: " + serviceType); + } + + @Override + public void onStartDiscoveryFailed(String serviceType, int errorCode) { + Log.e(TAG, "Discovery failed: Error code:" + errorCode); + mNsdManager.stopServiceDiscovery(this); + } + + @Override + public void onStopDiscoveryFailed(String serviceType, int errorCode) { + Log.e(TAG, "Discovery failed: Error code:" + errorCode); + mNsdManager.stopServiceDiscovery(this); + } + }; +} +</pre> + +<p>The NSD API uses the methods in this interface to inform your application when discovery +is started, when it fails, and when services are found and lost (lost means "is +no longer available"). Notice that this snippet does several checks +when a service is found.</p> +<ol> + <li>The service name of the found service is compared to the service +name of the local service to determine if the device just picked up its own +broadcast (which is valid).</li> +<li>The service type is checked, to verify it's a type of service your +application can connect to.</li> +<li>The service name is checked to verify connection to the correct +application.</li> +</ol> + +<p>Checking the service name isn't always necessary, and is only relevant if you +want to connect to a specific application. For instance, the application might +only want to connect to instances of itself running on other devices. However, if the +application wants to connect to a network printer, it's enough to see that the service type +is "_ipp._tcp".</p> + +<p>After setting up the listener, call {@link android.net.nsd.NsdManager#discoverServices(String, int, +NsdManager.DiscoveryListener) discoverServices()}, passing in the service type +your application should look for, the discovery protocol to use, and the +listener you just created.</p> + +<pre> + mNsdManager.discoverServices( + SERVICE_TYPE, NsdManager.PROTOCOL_DNS_SD, mDiscoveryListener); +</pre> + + +<h2 id="connect">Connect to Services on the Network</h2> +<p>When your application finds a service on the network to connect to, it +must first determine the connection information for that service, using the +{@link android.net.nsd.NsdManager#resolveService resolveService()} method. +Implement a {@link android.net.nsd.NsdManager.ResolveListener} to pass into this +method, and use it to get a {@link android.net.nsd.NsdServiceInfo} containing +the connection information.</p> + +<pre> +public void initializeResolveListener() { + mResolveListener = new NsdManager.ResolveListener() { + + @Override + public void onResolveFailed(NsdServiceInfo serviceInfo, int errorCode) { + // Called when the resolve fails. Use the error code to debug. + Log.e(TAG, "Resolve failed" + errorCode); + } + + @Override + public void onServiceResolved(NsdServiceInfo serviceInfo) { + Log.e(TAG, "Resolve Succeeded. " + serviceInfo); + + if (serviceInfo.getServiceName().equals(mServiceName)) { + Log.d(TAG, "Same IP."); + return; + } + mService = serviceInfo; + int port = mService.getPort(); + InetAddress host = mService.getHost(); + } + }; +} +</pre> + +<p>Once the service is resolved, your application receives detailed +service information including an IP address and port number. This is everything +you need to create your own network connection to the service.</p> + + +<h2 id="teardown">Unregister Your Service on Application Close</h2> +<p>It's important to enable and disable NSD +functionality as appropriate during the application's +lifecycle. Unregistering your application when it closes down helps prevent +other applications from thinking it's still active and attempting to connect to +it. Also, service discovery is an expensive operation, and should be stopped +when the parent Activity is paused, and re-enabled when the Activity is +resumed. Override the lifecycle methods of your main Activity and insert code +to start and stop service broadcast and discovery as appropriate.</p> + +<pre> +//In your application's Activity + + @Override + protected void onPause() { + if (mNsdHelper != null) { + mNsdHelper.tearDown(); + } + super.onPause(); + } + + @Override + protected void onResume() { + super.onResume(); + if (mNsdHelper != null) { + mNsdHelper.registerService(mConnection.getLocalPort()); + mNsdHelper.discoverServices(); + } + } + + @Override + protected void onDestroy() { + mNsdHelper.tearDown(); + mConnection.tearDown(); + super.onDestroy(); + } + + // NsdHelper's tearDown method + public void tearDown() { + mNsdManager.unregisterService(mRegistrationListener); + mNsdManager.stopServiceDiscovery(mDiscoveryListener); + } +</pre> + diff --git a/docs/html/training/connect-devices-wirelessly/wifi-direct.jd b/docs/html/training/connect-devices-wirelessly/wifi-direct.jd new file mode 100644 index 000000000000..99bb243879a0 --- /dev/null +++ b/docs/html/training/connect-devices-wirelessly/wifi-direct.jd @@ -0,0 +1,372 @@ +page.title=Connecting with Wi-Fi Direct +parent.title=Connecting Devices Wirelessly +parent.link=index.html + +trainingnavtop=true +previous.title=Using Network Service Discovery +previous.link=nsd.html +next.title=Service Discovery with Wi-Fi Direct +next.link=nsd-wifi-direct.html + +@jd:body + +<div id="tb-wrapper"> + <div id="tb"> + <h2>This lesson teaches you how to</h2> + <ol> + <li><a href="#permissions">Set Up Application Permissions</a></li> + <li><a href="#receiver">Set Up the Broadcast Receiver and Peer-to-Peer + Manager</a></li> + <li><a href="#discover">Initiate Peer Discovery</a></li> + <li><a href="#fetch">Fetch the List of Peers</a></li> + <li><a href="#connect">Connect to a Peer</a></li> + </ol> + </div> +</div> + +<p>The Wi-Fi Direct™ APIs allow applications to connect to nearby devices without +needing to connect to a network or hotspot. This allows your application to quickly +find and interact with nearby devices, at a range beyond the capabilities of Bluetooth. +</p> +<p> +This lesson shows you how to find and connect to nearby devices using Wi-Fi Direct. +</p> +<h2 id="permissions">Set Up Application Permissions</h2> +<p>In order to use Wi-Fi Direct, add the {@link +android.Manifest.permission#CHANGE_WIFI_STATE}, {@link +android.Manifest.permission#ACCESS_WIFI_STATE}, +and {@link android.Manifest.permission#INTERNET} +permissions to your manifest. Wi-Fi Direct doesn't require an internet connection, +but it does use standard Java sockets, which require the {@link +android.Manifest.permission#INTERNET} permission. +So you need the following permissions to use Wi-Fi Direct.</p> + +<pre> +<manifest xmlns:android="http://schemas.android.com/apk/res/android" + package="com.example.android.nsdchat" + ... + + <uses-permission + android:required="true" + android:name="android.permission.ACCESS_WIFI_STATE"/> + <uses-permission + android:required="true" + android:name="android.permission.CHANGE_WIFI_STATE"/> + <uses-permission + android:required="true" + android:name="android.permission.INTERNET"/> + ... +</pre> + +<h2 id="receiver">Set Up a Broadcast Receiver and Peer-to-Peer Manager</h2> +<p>To use Wi-Fi Direct, you need to listen for broadcast intents that tell your +application when certain events have occurred. In your application, instantiate +an {@link +android.content.IntentFilter} and set it to listen for the following:</p> +<dl> + <dt>{@link android.net.wifi.p2p.WifiP2pManager#WIFI_P2P_STATE_CHANGED_ACTION}</dt> + <dd>Indicates whether Wi-Fi Peer-To-Peer (P2P) is enabled</dd> + <dt>{@link android.net.wifi.p2p.WifiP2pManager#WIFI_P2P_PEERS_CHANGED_ACTION}</dt> + <dd>Indicates that the available peer list has changed.</dd> + <dt>{@link android.net.wifi.p2p.WifiP2pManager#WIFI_P2P_CONNECTION_CHANGED_ACTION}</dt> + <dd>Indicates the state of Wi-Fi P2P connectivity has changed.</dd> + <dt>{@link android.net.wifi.p2p.WifiP2pManager#WIFI_P2P_THIS_DEVICE_CHANGED_ACTION}</dt> + <dd>Indicates this device's configuration details have changed.</dd> +<pre> +private final IntentFilter intentFilter = new IntentFilter(); +... +@Override +public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.main); + + // Indicates a change in the Wi-Fi Peer-to-Peer status. + intentFilter.addAction(WifiP2pManager.WIFI_P2P_STATE_CHANGED_ACTION); + + // Indicates a change in the list of available peers. + intentFilter.addAction(WifiP2pManager.WIFI_P2P_PEERS_CHANGED_ACTION); + + // Indicates the state of Wi-Fi P2P connectivity has changed. + intentFilter.addAction(WifiP2pManager.WIFI_P2P_CONNECTION_CHANGED_ACTION); + + // Indicates this device's details have changed. + intentFilter.addAction(WifiP2pManager.WIFI_P2P_THIS_DEVICE_CHANGED_ACTION); + + ... +} +</pre> + + <p>At the end of the {@link android.app.Activity#onCreate onCreate()} method, get an instance of the {@link +android.net.wifi.p2p.WifiP2pManager}, and call its {@link +android.net.wifi.p2p.WifiP2pManager#initialize(Context, Looper, WifiP2pManager.ChannelListener) initialize()} +method. This method returns a {@link +android.net.wifi.p2p.WifiP2pManager.Channel} object, which you'll use later to +connect your app to the Wi-Fi Direct Framework.</p> + +<pre> +@Override + +Channel mChannel; + +public void onCreate(Bundle savedInstanceState) { + .... + mManager = (WifiP2pManager) getSystemService(Context.WIFI_P2P_SERVICE); + mChannel = mManager.initialize(this, getMainLooper(), null); +} +</pre> +<p>Now create a new {@link +android.content.BroadcastReceiver} class that you'll use to listen for changes +to the System's Wi-Fi P2P state. In the {@link +android.content.BroadcastReceiver#onReceive(Context, Intent) onReceive()} +method, add a condition to handle each P2P state change listed above.</p> + +<pre> + + @Override + public void onReceive(Context context, Intent intent) { + String action = intent.getAction(); + if (WifiP2pManager.WIFI_P2P_STATE_CHANGED_ACTION.equals(action)) { + // Determine if Wifi Direct mode is enabled or not, alert + // the Activity. + int state = intent.getIntExtra(WifiP2pManager.EXTRA_WIFI_STATE, -1); + if (state == WifiP2pManager.WIFI_P2P_STATE_ENABLED) { + activity.setIsWifiP2pEnabled(true); + } else { + activity.setIsWifiP2pEnabled(false); + } + } else if (WifiP2pManager.WIFI_P2P_PEERS_CHANGED_ACTION.equals(action)) { + + // The peer list has changed! We should probably do something about + // that. + + } else if (WifiP2pManager.WIFI_P2P_CONNECTION_CHANGED_ACTION.equals(action)) { + + // Connection state changed! We should probably do something about + // that. + + } else if (WifiP2pManager.WIFI_P2P_THIS_DEVICE_CHANGED_ACTION.equals(action)) { + DeviceListFragment fragment = (DeviceListFragment) activity.getFragmentManager() + .findFragmentById(R.id.frag_list); + fragment.updateThisDevice((WifiP2pDevice) intent.getParcelableExtra( + WifiP2pManager.EXTRA_WIFI_P2P_DEVICE)); + + } + } +</pre> + +<p>Finally, add code to register the intent filter and broadcast receiver when +your main activity is active, and unregister them when the activity is paused. +The best place to do this is the {@link android.app.Activity#onResume()} and +{@link android.app.Activity#onPause()} methods. + +<pre> + /** register the BroadcastReceiver with the intent values to be matched */ + @Override + public void onResume() { + super.onResume(); + receiver = new WiFiDirectBroadcastReceiver(mManager, mChannel, this); + registerReceiver(receiver, intentFilter); + } + + @Override + public void onPause() { + super.onPause(); + unregisterReceiver(receiver); + } +</pre> + + +<h2 id="discover">Initiate Peer Discovery</h2> +<p>To start searching for nearby devices with Wi-Fi Direct, call {@link +android.net.wifi.p2p.WifiP2pManager#discoverPeers(WifiP2pManager.Channel, +WifiP2pManager.ActionListener) discoverPeers()}. This method takes the +following arguments:</p> +<ul> + <li>The {@link android.net.wifi.p2p.WifiP2pManager.Channel} you + received back when you initialized the peer-to-peer mManager</li> + <li>An implementation of {@link android.net.wifi.p2p.WifiP2pManager.ActionListener} with methods + the system invokes for successful and unsuccessful discovery.</li> +</ul> + +<pre> +mManager.discoverPeers(mChannel, new WifiP2pManager.ActionListener() { + + @Override + public void onSuccess() { + // Code for when the discovery initiation is successful goes here. + // No services have actually been discovered yet, so this method + // can often be left blank. Code for peer discovery goes in the + // onReceive method, detailed below. + } + + @Override + public void onFailure(int reasonCode) { + // Code for when the discovery initiation fails goes here. + // Alert the user that something went wrong. + } +}); +</pre> + +<p>Keep in mind that this only <em>initiates</em> peer discovery. The +{@link android.net.wifi.p2p.WifiP2pManager#discoverPeers(WifiP2pManager.Channel, +WifiP2pManager.ActionListener) discoverPeers()} method starts the discovery process and then +immediately returns. The system notifies you if the peer discovery process is +successfully initiated by calling methods in the provided action listener. +Also, discovery will remain active until a connection is initiated or a P2P group is +formed.</p> + +<h2 id="fetch">Fetch the List of Peers</h2> +<p>Now write the code that fetches and processes the list of peers. First +implement the {@link android.net.wifi.p2p.WifiP2pManager.PeerListListener} +interface, which provides information about the peers that Wi-Fi Direct has +detected. The following code snippet illustrates this.</p> + +<pre> + private List<WifiP2pDevice> peers = new ArrayList<WifiP2pDevice>(); + ... + + private PeerListListener peerListListener = new PeerListListener() { + @Override + public void onPeersAvailable(WifiP2pDeviceList peerList) { + + // Out with the old, in with the new. + peers.clear(); + peers.addAll(peerList.getDeviceList()); + + // If an AdapterView is backed by this data, notify it + // of the change. For instance, if you have a ListView of available + // peers, trigger an update. + ((WiFiPeerListAdapter) getListAdapter()).notifyDataSetChanged(); + if (peers.size() == 0) { + Log.d(WiFiDirectActivity.TAG, "No devices found"); + return; + } + } + } +</pre> + +<p>Now modify your broadcast receiver's {@link +android.content.BroadcastReceiver#onReceive(Context, Intent) onReceive()} +method to call {@link android.net.wifi.p2p.WifiP2pManager#requestPeers +requestPeers()} when an intent with the action {@link +android.net.wifi.p2p.WifiP2pManager#WIFI_P2P_PEERS_CHANGED_ACTION} is received. You +need to pass this listener into the receiver somehow. One way is to send it +as an argument to the broadcast receiver's constructor. +</p> + +<pre> +public void onReceive(Context context, Intent intent) { + ... + else 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() + if (mManager != null) { + mManager.requestPeers(mChannel, peerListener); + } + Log.d(WiFiDirectActivity.TAG, "P2P peers changed"); + }... +} +</pre> + +<p>Now, an intent with the action {@link +android.net.wifi.p2p.WifiP2pManager#WIFI_P2P_PEERS_CHANGED_ACTION} intent will +trigger a request for an updated peer list. </p> + +<h2 id="connect">Connect to a Peer</h2> +<p>In order to connect to a peer, create a new {@link +android.net.wifi.p2p.WifiP2pConfig} object, and copy data into it from the +{@link android.net.wifi.p2p.WifiP2pDevice} representing the device you want to +connect to. Then call the {@link +android.net.wifi.p2p.WifiP2pManager#connect(WifiP2pManager.Channel, +WifiP2pConfig, WifiP2pManager.ActionListener) connect()} +method.</p> + +<pre> + @Override + public void connect() { + // Picking the first device found on the network. + WifiP2pDevice device = peers.get(0); + + WifiP2pConfig config = new WifiP2pConfig(); + config.deviceAddress = device.deviceAddress; + config.wps.setup = WpsInfo.PBC; + + mManager.connect(mChannel, config, new ActionListener() { + + @Override + public void onSuccess() { + // WiFiDirectBroadcastReceiver will notify us. Ignore for now. + } + + @Override + public void onFailure(int reason) { + Toast.makeText(WiFiDirectActivity.this, "Connect failed. Retry.", + Toast.LENGTH_SHORT).show(); + } + }); + } +</pre> + +<p>The {@link android.net.wifi.p2p.WifiP2pManager.ActionListener} implemented in +this snippet only notifies you when the <em>initiation</em> succeeds or fails. +To listen for <em>changes</em> in connection state, implement the {@link +android.net.wifi.p2p.WifiP2pManager.ConnectionInfoListener} interface. Its {@link +android.net.wifi.p2p.WifiP2pManager.ConnectionInfoListener#onConnectionInfoAvailable(WifiP2pInfo) +onConnectionInfoAvailable()} +callback will notify you when the state of the connection changes. In cases +where multiple devices are going to be connected to a single device (like a game with +3 or more players, or a chat app), one device will be designated the "group +owner".</p> + +<pre> + @Override + public void onConnectionInfoAvailable(final WifiP2pInfo info) { + + // InetAddress from WifiP2pInfo struct. + InetAddress groupOwnerAddress = info.groupOwnerAddress.getHostAddress()); + + // After the group negotiation, we can determine the group owner. + if (info.groupFormed && info.isGroupOwner) { + // Do whatever tasks are specific to the group owner. + // One common case is creating a server thread and accepting + // incoming connections. + } else if (info.groupFormed) { + // The other device acts as the client. In this case, + // you'll want to create a client thread that connects to the group + // owner. + } + } +</pre> + +<p>Now go back to the {@link +android.content.BroadcastReceiver#onReceive(Context, Intent) onReceive()} method of the broadcast receiver, and modify the section +that listens for a {@link +android.net.wifi.p2p.WifiP2pManager#WIFI_P2P_CONNECTION_CHANGED_ACTION} intent. +When this intent is received, call {@link +android.net.wifi.p2p.WifiP2pManager#requestConnectionInfo(WifiP2pManager.Channel, +WifiP2pManager.ConnectionInfoListener) requestConnectionInfo()}. This is an +asynchronous call, so results will be received by the connection info listener +you provide as a parameter. + +<pre> + ... + } else if (WifiP2pManager.WIFI_P2P_CONNECTION_CHANGED_ACTION.equals(action)) { + + if (mManager == null) { + return; + } + + NetworkInfo networkInfo = (NetworkInfo) intent + .getParcelableExtra(WifiP2pManager.EXTRA_NETWORK_INFO); + + if (networkInfo.isConnected()) { + + // We are connected with the other device, request connection + // info to find group owner IP + + mManager.requestConnectionInfo(mChannel, connectionListener); + } + ... +</pre> diff --git a/docs/html/training/training_toc.cs b/docs/html/training/training_toc.cs index 37b69d4c8c70..b70ba3f4828c 100644 --- a/docs/html/training/training_toc.cs +++ b/docs/html/training/training_toc.cs @@ -673,6 +673,27 @@ </li> + <li class="nav-section"> + <div class="nav-section-header"><a href="<?cs var:toroot ?>training/connect-devices-wirelessly/index.html"> + <span class="en">Connecting Devices Wirelessly</span> + </a></div> + <ul> + <li><a href="<?cs var:toroot ?>training/connect-devices-wirelessly/nsd.html"> + <span class="en">Using Network Service Discovery</span> + </a> + </li> + <li><a href="<?cs var:toroot ?>training/connect-devices-wirelessly/wifi-direct.html"> + <span class="en">Connecting with Wi-Fi Direct</span> + </a> + </li> + <li><a href="<?cs var:toroot ?>training/connect-devices-wirelessly/nsd-wifi-direct.html"> + <span class="en">Using Wi-Fi Direct for Service Discovery</span> + </a> + </li> + </ul> + </li> + + </ul> </li> </ul><!-- nav --> |