1 of 75

Android Connectivity

Juan E. Vargas

https://goo.gl/ddLM9h

2 of 75

Apologies to Antartica

3 of 75

4 of 75

5 of 75

6 of 75

7 of 75

Interior Maps

Matterhorn Focus

Go to “See inside”

8 of 75

Android devices can be connected to

  1. Other devices nearby via (NSD USB, Bluetooth),...
  2. … other devices via P2P Wi-Fi
  3. … the Internet via Wi-Fi, ...
  4. … a phone service, …
  5. … the cloud...

Android devices can be passive clients or active servers running their own networking services.

The broad spectrum of possibilities makes the topic of Android connectivity subtle and quite complex.

9 of 75

NSD

Android has under its .net api a set of libraries for NSD (Network Service Discovery). These libraries are used to find devices and to connect to some of the local devices found.

Once such devices are found, connectivity can be established using USB, Bluetooth and/or Wi-Fi.

We will explore the following topics:

  1. Using NSD
  2. Creating P2P (point to point) connections
  3. Using Wi-Fi and/or P2P for Service Discovery
  4. Using Bluetooth

10 of 75

NSD

Network Service Discovery (NSD) allows apps to discover and identify other devices on the local network that support services that our apps may need or request.

This is useful for apps that require P2P communication for file sharing, multi-player gaming, etc.

Developers can use the NSD API to broadcast names and connection information to the local network and to get information from other applications doing the same.

Once a connection is established, communicating with the same app running on a different device is possible.

11 of 75

NSD

These are the steps needed:

  1. Create Service
  2. Register service on Network
  3. Discover Services on the Network
  4. Connect to Services on the Network
  5. Unregister your Service on Application Close

In most cases code development for these steps consist of overriding methods from the android.net API classes.

This involves (1) code reuse and (2) following best practices examples. Developers need not ask what they can do for Android connectivity but instead ask what Android connectivity can do for them … ;-)

Developers who understand the mechanics of method overriding can become experts quickly as long as they follow the patterns embedded in the API

12 of 75

Standards and Best Practices

IANA is an international org that manages a centralized and authoritative list of service types used by service discovery protocols such as NSD.

You may need to get the list from IANA for service names and port numbers. If you are creating a new service type, you may need to reserve it by filling out the IANA Ports and Service Registration form.

When setting a port number for a service in your app, DO NOT HARDCODE the number. There is no need for the port number to be known by other apps at compile-time because this practice will likely create conflicts with other applications and also become a honey trap (not to be confused with a honeypot) for viral infections.

Instead use the device's next available port. This information will be provided at run-time to other apps wishing to connect as a service broadcast. The apps can get this information from your service broadcast, right before connecting to your service.

http://www.iana.org/

http://www.iana.org/assignments/service-names-port-numbers/service-names-port-numbers.xml

13 of 75

  1. Create Service Obj for Network

This step is necessary only if the app is a service that needs to broadcast over the local network.

To create and register a service on the local network, a NsdServiceInfo object is needed. This object provides the information that other devices on the network will use to connect to your service.

public void registerService(int port) {

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);

....

}

"NsdChat" becomes a service visible to any device on the network that is using NSD to search for local services.

Name must be unique on the network. Android automatically handles conflict resolution, e.g., a second service of same name becomes "NsdChat (1)".

_protocol._trasportlayer

See also:

https://developer.android.com/reference/android/net/nsd/NsdManager.html

14 of 75

End Points

A network socket is an endpoint of a P2P communication across a computer network.

Most often the Internet Protocol (IP) is used.

Therefore you will work mostly with TCP/IP Sockets.

15 of 75

16 of 75

Initializing Sockets

public void initializeServerSocket() {

// Initialize a server socket on the next available port.

mServerSocket = new ServerSocket(0);

// Store the chosen port.

mLocalPort = mServerSocket.getLocalPort();

...

}

Entering (0) initializes the socket to the the “next available port”

now you know your port number

17 of 75

2. Registering the NSD Object

The NsdServiceInfo object needs to be registered. This is done with a RegistrationListener interface, which contains callbacks to alert your app of the success/failure of service registration/unregistration.

public void initializeRegistrationListener() {

mRegistrationListener = new NsdManager.RegistrationListener() {

@Override

public void onServiceRegistered(NsdServiceInfo NsdServiceInfo) {

// App needs to get serviceName because Android may have changed it to resolve conflicts.

mServiceName = NsdServiceInfo.getServiceName();

}

@Override

public void onRegistrationFailed(NsdServiceInfo serviceInfo, int errorCode) {

// Registration failed! Enter code to determine why.

}

@Override

public void onServiceUnregistered(NsdServiceInfo arg0) {

// Service has been unregistered. This happens when app calls NsdManager.unregisterService() and passes in this listener.

}

@Override

public void onUnregistrationFailed(NsdServiceInfo serviceInfo, int errorCode) {

// Unregistration failed. Enter code to determine why.

}

};

}

DO SOMETHING!

18 of 75

2. Registering the Service

Once the NSDInfo object and the sockets are ready the service needs to be registered via a registerService() method, such as the one shown below, which is asynchronous.

This means that any code that needs to run after the service has been registered must go in the onServiceRegistered() event listener method, which is a method that the service developer needs to specialize

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);

}

Not shown here

19 of 75

At this point we have...

SERVER

  1. Create Service
    1. NsdServiceInfo object
    2. Create NewServiceSocket
    3. Create Port
  2. Register Service on Network
    • Register NsdServiceInfo object
  3. Wait for service requests
    • Process service requests
  4. Unregister Service on Application Close

CLIENT

  1. Discover Services on the Network
    1. NsdManager.DiscoveryListener
    2. Discover services ‘worth-connecting’ using resolveService
  2. Connect to Services
  3. Emit service requests
  4. Process service responses
  5. Unregister Service on Application Close

20 of 75

3. Discovering Services on Network

“ … 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…”

Client apps need to listen to service broadcasts on the network to determine what services are available and are worth connecting to.

Service discovery involves (1) setting up a discovery listener with its relevant callbacks, and (2) making a single asynchronous API call to discoverServices().

21 of 75

Discovery Listener

public void initializeDiscoveryListener() {

mDiscoveryListener = new NsdManager.DiscoveryListener() {

@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 users what they'd be connecting to.

Log.d(TAG, "Same machine: " + mServiceName);

} else if (service.getServiceName().contains("NsdChat")){

mNsdManager.resolveService(service, mResolveListener);

}

}

Creates anonymous method as a closure

instantiate nsd DiscoveryListener

called as soon as svc begins

22 of 75

Discovery Listener

public void initializeDiscoveryListener() {

  1. . . . . ..

@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);

}

These methods need to be implemented with the specific details for the app in mind

23 of 75

4. Connecting to Services

Once a worth-connecting service is found, an explicit connection needs to be initiated at the client app.

To do that the app needs to examine the connection information for that service via resolveService().

The app needs to implement a NsdManager.ResolveListener() and use it to get the connection information.

24 of 75

4. Connecting to Services

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();

}

}; // end of listener object

} // end of method

where are the mThings kept?

25 of 75

At this point we have...

SERVER

  • Create Service
    • NsdServiceInfo object
    • Create NewServiceSocket
    • Create Port
  • Register Service on Network
    • Register NsdServiceInfo object
  • Wait for service requests
    • Process service requests
  • Unregister Service on Application Close

CLIENT

  • Discover Services on the Network
    • NsdManager.DiscoveryListener
    • Discover services ‘worth-connecting’ using resolveService
  • Connect to Services
  • Emit service requests
  • Process service responses
  • Unregister Service on Application Close

26 of 75

5. Unregister a Service on Application Close

It is important to enable and disable NSD as needed during the app lifecycle. Unregistering the service when the app server closes prevents other apps from attempting to connect to it.

Service discovery is an expensive operation that should be stopped when the parent activity is paused and re-enabled when the activity is resumed.

This is done by overriding the lifecycle methods of the main activity and inserting code to start and stop service broadcast and discovery as appropriate.

27 of 75

@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);

}

This code goes in app’s Activity

where are the mThings kept?

28 of 75

At this point we have...

SERVER

  • Create Service
    • NsdServiceInfo object
    • Create NewServiceSocket
    • Create Port
  • Register Service on Network
    • Register NsdServiceInfo object
  • Wait for service requests
    • Process service requests
  • Unregister Service on Application Close

CLIENT

  • Discover Services on the Network
    • NsdManager.DiscoveryListener
    • Discover services ‘worth-connecting’ using resolveService
  • Connect to Services
  • Emit service requests
  • Process service responses
  • Unregister Service on Application Close

29 of 75

30 of 75

31 of 75

android.net

There are several android-specific classes under this base-class that provide services above and beyond what you have seen in the java.net.* APIs

http://developer.android.com/reference/android/net/package-summary.html

32 of 75

33 of 75

34 of 75

35 of 75

P2P Connections with Wi-Fi

The android.net.wifi.p2p is an Android P2P API that provides apps the ability to connect to a network or a hotspot via Wi-Fi.

WiFi P2P allows apps to interact with nearby devices over a range beyond Bluetooth.

As before, there are several areas in the app code that need to be touched to make this happen.

36 of 75

… much more

37 of 75

38 of 75

SetUp Application Permissions

The following permissions need to be set in the app manifest:

CHANGE_WIFI_STATE ACCESS_WIFI_STATE and INTERNET

<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"/>

...

39 of 75

Receiving Messages

In Android, broadcast messages take the form of intents that notify apps the occurrence of certain events.

Events in Android are detected via listeners.

The app needs to set up two listeners: IntentFilter and Brodcastreceiver.

40 of 75

IntentFilter

WIFI_P2P_STATE_CHANGED_ACTION

Is Wi-Fi P2P enabled ?

WIFI_P2P_PEERS_CHANGED_ACTION

Has the available peer list changed?

WIFI_P2P_CONNECTION_CHANGED_ACTION

Has the state of Wi-Fi P2P connectivity changed?

WIFI_P2P_THIS_DEVICE_CHANGED_ACTION

Has device configuration details changed?

41 of 75

Overriding IntentFilter Methods

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 P2P 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);

...

}

42 of 75

Besides the overriding mechanics, we may envision listeners as an extension of the

try/catch structure...

…. on steroids...

43 of 75

WiFiP2pManager

At the end of the activity onCreate( ) an instance of WifiP2pManager needs to be created and immediately after a call to its initialize() method must be issued.

The channel object returned is the one that will be used by the app to connect to the Wi-Fi P2P framework.

Channel mChannel;

public void onCreate(Bundle savedInstanceState) {

.... // do stuff...

mManager = (WifiP2pManager) getSystemService(Context.WIFI_P2P_SERVICE);

mChannel = mManager.initialize(this, getMainLooper(), null);

}

44 of 75

BroadcastReceiver Object

The next step is to create a new object BroadcastReceiver that will listen for changes to the System's Wi-Fi P2P state.

This object will have an onReceive() method, and code/conditions to handle each of the four P2P actions (state changes) listed before.

45 of 75

@Override

public void onReceive(Context context, Intent intent) {

String action = intent.getAction();

if (WifiP2pManager.WIFI_P2P_STATE_CHANGED_ACTION.equals(action)) {

// Determine if Wifi P2P 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)) {

// Peer list has changed! Do something about that.

} else if (WifiP2pManager.WIFI_P2P_CONNECTION_CHANGED_ACTION.equals(action)) {

// Connection state changed! 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));

}

}

Where is this method created?????

46 of 75

Register Filter and Receiver

Next step is to register the intent filter and the broadcast receiver when the main activity is active.

Also, these objects need to get unregistered when the activity is paused. The best place to do the registration and the unregistration is the onResume() and onPause() methods.

47 of 75

OnResume and OnPause

/** 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);

}

Where are these method created?????

48 of 75

Peer Discovery

Apps search for nearby devices with Wi-Fi P2P using discoverPeers(). This method takes two arguments:

  1. The WifiP2pManager.Channel received when the P2P manager was initialized
  2. The WifiP2pManager.ActionListener object with methods that the system invokes for successful and unsuccessful discovery.

49 of 75

mManager.discoverPeers(mChannel, new WifiP2pManager.ActionListener() {

@Override

public void onSuccess() {

// Code to process successful discovery initiation.

// No services are actually been discovered yet, so this method

// can often be left blank. Code for peer discovery goes in the

// onReceive method, described later on another slide.

}

@Override

public void onFailure(int reasonCode) {

// Code template to process discovery initiation failures.

// Alert the user that something went wrong.

}

});

Remember that this only initiates the peer discovery process. The method just gets the discovery process launched and immediately returns. The system notifies the app via the action listeners.

Note also that discovery will remain active until a connection is initiated or a P2P group is formed.

50 of 75

List of Peers

Likely there will be more than one response from peers. The next step is to get the list of peers, process the list, and select the peer the app will connect to.

This is easier done than said, by implementing a WifiP2pManager.PeerListListener interface, which provides information about the peers detected by the Wi-Fi P2P

51 of 75

private List peers = new ArrayList();

...

private PeerListListener peerListListener = new PeerListListener() {

@Override

public void onPeersAvailable(WifiP2pDeviceList peerList) {

// Clear and refresh list

peers.clear();

peers.addAll(peerList.getDeviceList());

// If an AdapterView is backed by this data, notify it about 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;

}

}

}

Where is this interface created?????

52 of 75

OnReceive

The broadcast receiver's onReceive() method calls requestPeers() when an intent with the action WIFI_P2P_PEERS_CHANGED_ACTION is received.

You must pass this listener into the receiver, e.g., sending it as an argument to the broadcast receiver's constructor.

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 call is asynchronous call.

// The calling activity is notified with a callback on PeerListListener.onPeersAvailable()

if (mManager != null) {

mManager.requestPeers(mChannel, peerListListener);

}

Log.d(WiFiDirectActivity.TAG, "P2P peers changed");

}...

}

53 of 75

Connecting to a Peer

Connections to peers are established through WifiP2pConfig objects. �

First an object of the class is constructed, then parameters are passed, and then the connect () method is called.

54 of 75

Overriding Connect ()

@Override

public void connect() {

WifiP2pDevice device = peers.get(0); // get first device found on network.

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();

}

} ) ;

}

55 of 75

Listen for changes in Connection State

The WifiP2pManager.ActionListener above only notifies when the initiation succeeds or fails.

To listen for changes in connection state, an implementation of a WifiP2pManager.ConnectionInfoListener interface is needed.

Its onConnectionInfoAvailable() callback will notify when the state of the connection changes.

In cases where multiple devices are going to be connected to a single device one device will be designated as the "group owner".

56 of 75

@Override

public void onConnectionInfoAvailable(final WifiP2pInfo info) {

// InetAddress from WifiP2pInfo struct.

InetAddress groupOwnerAddress = info.groupOwnerAddress.getHostAddress());

// After group negotiation, determine group owner.

if (info.groupFormed && info.isGroupOwner) {

// Do whatever tasks are specific to the group owner.

// One common case is creating a server thread to accept incoming connections.

} else if (info.groupFormed) {

// The other device acts as the client. In this case, create a client

// thread to connect to the group owner.

}

}

57 of 75

Addition to OnReceive

The code in next slide needs to be added to the broadcastreceiver onReceive() method to listen for the intent

WIFI_P2P_CONNECTION_CHANGED_ACTION

When the intent is received, the code calls requestConnectionInfo().

This is an asynchronous call; results will be received by the connection info listener provided as a parameter.

58 of 75

...

} 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()) {

// App is connected with the other device(s), request connection

// info to find group owner IP

mManager.requestConnectionInfo(mChannel, connectionListener);

}

...

59 of 75

Using Wi-Fi P2P for Service Discovery

We have seen how NSD can be used to discover services connected to a local network.

Wi-Fi P2P Service Discovery allows apps to discover nearby services directly without being connected to a network.

Also, services (on your device) can be advertised

60 of 75

61 of 75

62 of 75

63 of 75

BlueTooth Code

64 of 75

BlueTooth Chat

65 of 75

Three Java Classes

66 of 75

4 Layouts

67 of 75

BluetoothChat Java Class

  1. Extends Activity
  2. Declares constants (IDs) for messages, device names request, etc.
  3. Declares the Layouts
  4. Sets up buffers for message exchange
  5. Declares Bluetooth adapter object and initializes it
  6. Overrides the following methods: OnCreate, OnStart, OnResume, OnPause, OnStop, OnDestroy (for activity)
  7. Overrides the following methods for menus: OnCreateMeu, OnSelectedItem
  8. Defines Methods setupChat(), sendMessage(), setStatus(),
  9. Defines a message handler
  10. Defines TextView Listener
  11. Defines OnActivityResults()
  12. Defines ConnectDevice()

68 of 75

BluetoothChat Service Java Class

Sets up and manages the BT connections with other devices.

This class defines a thread class and uses three threads: one listens incoming connections, another connects to other devices, and the third is used for data transmission

69 of 75

Update Android Studio

If your code fails after updating the Android Studio and you get some error message suggesting that something is wrong with Gradle, try this:

minifyEnabled false

instead of

runProguard false

70 of 75

… NsdChat

71 of 75

72 of 75

NsdChat Code

73 of 75

Google Cloud Messaging (GCM)

GCM is a free service designed for sending messages to Android devices. GCM messaging can enhance users experience because apps can stay updated without wasting battery power on waking up the radio and polling the server when there are no updates.

GCM has generous allocations. It gives developers permission to attach up to 1K recipients to a message, this enables developers to contact large user base when needed without affecting the work load on their servers.

This topic is to be discussed on a different file, named Android GCM

74 of 75

Client / Server Interactions

SERVER

  • Create Service
    • NsdServiceInfo object
    • Create NewServiceSocket
    • CreatePort
  • Register Service on Network
    • Register NsdServiceInfo object
  • Wait for service requests
    • Process service requests
  • Unregister Service on Application Close

CLIENT

  • Discover Services on the Network
    • NsdManager.DiscoveryListener
    • Discover services ‘worth-connecting’ using resolveService
  • Connect to Services
  • Emit service requests
  • Process service responses
  • Unregister your Service on Application Close

75 of 75

Synergy KVM