}

return super.shouldOverrideUrlLoading(view, url);

}

};

webView.setWebViewClient(webViewClient);

webView.getSettings().setJavaScriptEnabled(true);

webView.loadUrl(authorizationUrl);

}

class GetTokenAccess extends AsyncTask<Verifier, Void, Token> {

@Override

protected Token doInBackground(Verifier... verifiers) {

Token accessToken = mOAuthService.

getAccessToken(EMPTY_TOKEN, verifiers[0]);

return accessToken;

}

@Override

protected void onPostExecute(Token token) {

if (token != null) {

Intent intent = new Intent();

intent.putExtra(ACCESS_TOKEN, token.getToken());

setResult(RESULT_OK, intent);

} else {

setResult(RESULT_CANCELED);

}

finish();

}

}

}

The actual authentication and authorization toward Foursquare goes through a custom WebViewClient. The

custom WebViewClient overrides the method shouldOverrideUrlLoading(), which allows you to

capture all calls to a URL that this WebView will do. In this case, you monitor for all calls that match the callback

URL, which will contain the access token needed for signing API requests.

The example needs to use an AsyncTask because you cannot do direct network calls on the main thread.

OAuthService.getAccessToken() will perform a network call to retrieve the access token used for the

API calls that require a signed-in user.

The following code uses the Activity from the previous example:

public class FoursquareActivity extends Activity {

public static final String TAG = “FoursquareActivity”;

Chapter 17: Networking, Web Service, and Remote APIs 335

public static final int OAUTH_REQUEST_CODE = 1001;

public static final String FRIENDS_URI =

“https://api.foursquare.com/v2/users/self/friends”;

private SharedPreferences mPreferences;

@Override

protected void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.foursquare_main);

mPreferences = PreferenceManager.

getDefaultSharedPreferences(this);

}

@Override

protected void onResume() {

super.onResume();

if (mPreferences.contains(OAuthActivity.ACCESS_TOKEN)) {

new GetFoursquareFriends().execute(“55.59612590”, “12.98140870”);

} else {

startActivityForResult(new Intent(this, OAuthActivity.class),

OAUTH_REQUEST_CODE);

}

}

@Override

protected void onActivityResult(int requestCode,

int resultCode,

Intent data) {

if (requestCode == OAUTH_REQUEST_CODE) {

if (resultCode == RESULT_OK) {

String accesToken = data.

getStringExtra(OAuthActivity.ACCESS_TOKEN);

mPreferences.edit().

putString(OAuthActivity.ACCESS_TOKEN,

accesToken).apply();

} else {

mPreferences.edit().

remove(OAuthActivity.ACCESS_TOKEN).apply();

}

}

}

class GetFoursquareFriends extends AsyncTask<String, Void, JSONObject> {

@Override

protected JSONObject doInBackground(String... lngLat) {

OAuthService service = new ServiceBuilder()

.provider(Foursquare2Api.class)

.apiKey(OAuthActivity.CLIENT_ID)

.apiSecret(OAuthActivity.CLIENT_SECRET)

.callback(“oauth://foursquare”)

.build();

336 Part III: Pushing the Limits

String accessToken = mPreferences.

getString(OAuthActivity.ACCESS_TOKEN, null);

OAuthRequest request = new OAuthRequest(Verb.GET,

FRIENDS_URI);

request.addQuerystringParameter(“oauth_token”,

accessToken);

Token token = new Token(accessToken,

OAuthActivity.CLIENT_SECRET);

service.signRequest(token, request);

Response response = request.send();

if (response.isSuccessful()) {

try {

return new JSONObject(response.getBody());

} catch (JSONException e) {

Log.e(TAG, “Error building JSONObjet!”, e);

}

} else {

Log.d(TAG, “Bad request: “

+ response.getCode()

+ “ “

+ response.getMessage());

}

return null;

}

@Override

protected void onPostExecute(JSONObject response) {

if (response != null) {

try {

JSONArray friends = response.

getJSONObject(“response”).

getJSONObject(“friends”).

getJSONArray(“items”);

Log.d(TAG, “Friends: “ + friends);

} catch (JSONException e) {

Log.e(TAG, “JSON Exception”, e);

}

}

}

}

}

You start by checking whether an access token is already stored; if not, the OAuth2 Activity is started with

Activity.startActivityForResult(). When the OAuth2 process is completed, successful or not,

onActivityResult() from the preceding example is called with the results.

Chapter 17: Networking, Web Service, and Remote APIs 337

Using the AsyncTask from the preceding example, you can set up an OAuthRequest with the new access

token. In this case, an array of all the user’s friends on Foursquare is retrieved.

I use the preceding code only to illustrate how to use Scribe and OAuth2 in your application. Because

OAuth2 requires user interaction, this is one of the few cases where you must perform network

operations from an Activity.

You can use the Scribe library for any API that supports OAuth2 for authorization. Check the documentation of

the library for officially supported services.

Facebook SDK for Android

Because OAuth2 was designed for the web, it’s not a perfect match for a native Android application. Many

services have solved this problem in a similar way using a native Android app instead. For instance, Facebook

has a great web service that allows you to integrate its Graph API and other Facebook APIs into your Android

application. Because users who want to use the Facebook integration in your application most likely will have the

Facebook app installed as well, they perform a similar authorization process by using Android Activities.

The Facebook SDK for Android (version 3.0 and later) allows you to easily integrate Facebook authorization and

authentication for your application using its library project. You can then use this API to simply track users in

your application or integrate with Facebook services and send messages and photos. The Facebook SDK for

Android is available at https://developers.facebook.com/android.

First, you need to register your application on the Facebook Developer site (https://developers.

facebook.com/apps). Click Create New App and enter the name of your app while leaving the rest of the

fields empty or in their default. Next, open the Native Android app section and fill in the details related to your

app, as shown in Figure 17-2.

The key hash for your application is generated with the following command in your terminal:

$ keytool -exportcert -alias androiddebugkey –keystore ~/.android/debug.

keystore | openssl sha1 -binary | openssl base64

When the online registration is complete, copy the app ID and add it as a String resource in your application’s

resources. Then you add a metadata element that points to this value as well as a reference to the Facebook

LoginActivity.

338 Part III: Pushing the Limits

Figure 17-2 Creating a new Facebook app with Android support using the Facebook Developer console

Here is a snippet from the AndroidManifest.xml where the necessary components and metadata for

Facebook integration (marked in bold) are added:

<application

android:allowBackup=”true”

android:icon=”@drawable/ic_launcher”

android:label=”@string/app_name”

android:theme=”@style/AppTheme” >

<uses-permission android:name=”android.permission.INTERNET” />

<activity

android:name=”com.aptl.myfacebookdemo.MainActivity”

android:label=”@string/app_name” >

<intent-filter>

<action android:name=”android.intent.action.MAIN” />

<category android:name=”android.intent.category.LAUNCHER” />

</intent-filter>

</activity>

Chapter 17: Networking, Web Service, and Remote APIs 339

<activity android:name=”com.facebook.LoginActivity”/>

<meta-data android:name=”com.facebook.sdk.ApplicationId”

android:value=”@string/facebook_app_id” />

</application>

Next, you need to implement the Facebook callbacks in MainActivity. Also, don’t forget to add the

INTERNET permission as just shown.

The UiLifecycleHelper class takes care of all the state changes related to your Activity states. All

you need to do is override the onCreate(), onResume(), onActivityResult(), onPause(), and

onDestroy() and do the equivalent callback on the helper class, as shown here:

public class MainActivity extends Activity

implements Session.StatusCallback {

private UiLifecycleHelper mUiLifecycleHelper;

@Override

protected void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.activity_main);

LoginButton authButton =

(LoginButton) findViewById(R.id.authButton);

authButton.setReadPermissions(Arrays.asList(“user_birthday”,

“friends_birthday”));

mUiLifecycleHelper = new UiLifecycleHelper(this, this);

mUiLifecycleHelper.onCreate(savedInstanceState);

}

@Override

protected void onResume() {

super.onResume();

mUiLifecycleHelper.onResume();

}

@Override

protected void onPause() {

super.onPause();

mUiLifecycleHelper.onPause();

}

@Override

protected void onDestroy() {

super.onDestroy();

mUiLifecycleHelper.onDestroy();

}

@Override

protected void onSaveInstanceState(Bundle outState) {

super.onSaveInstanceState(outState);

mUiLifecycleHelper.onSaveInstanceState(outState);

}

340 Part III: Pushing the Limits

@Override

protected void onActivityResult(int requestCode, int resultCode,

Intent data) {

super.onActivityResult(requestCode, resultCode, data);

mUiLifecycleHelper.onActivityResult(requestCode, resultCode,

data);

}

@Override

public void call(Session session, SessionState state,

Exception exception) {

// Callback for session state changes...

}

}

Note the LoginButton that’s added to the layout for this Activity. You can use it to provide a default

Facebook Login button to your application. It’s also possible to pass extra parameters, such as necessary

permissions and such, which will then be passed on to the authentication process. When the user taps the Login

button, a dialog box appears asking the user if she wants to allow your application to get the permissions it has

requested, as shown in Figure 17-3.

Figure 17-3 Dialog box asking the user for permissions

to access her specific Facebook data

Chapter 17: Networking, Web Service, and Remote APIs 341

You can add the Facebook Login button in the XML layout for your Activity as shown in the following. This

way, you can control where the button should appear while at the same time provide a familiar entry point for

the user to log in to Facebook.

<com.facebook.widget.LoginButton

android:id=”@+id/authButton”

android:layout_width=”wrap_content”

android:layout_height=”wrap_content”

android:layout_gravity=”center_horizontal”

android:layout_marginTop=”5dp” />

Once you have a valid authentication toward Facebook, you can use the Request class from the Facebook

SDK to perform requests. The methods provided by this class come in many different versions, some are

asynchronous and perform the network call on a separate background thread, while others are synchronous

and allow you to control the background threading yourself. In the following code example you can see a

request for the user’s friends made using the asynchronous method.

public void doLoadFriendList(View view) {

if(SessionState.OPENED_TOKEN_UPDATED.

equals(mSessionState)

|| SessionState.OPENED.equals(mSessionState)) {

Request.executeMyFriendsRequestAsync(mSession,

new Request.GraphUserListCallback() {

@Override

public void onCompleted(List<GraphUser> friends,

Response response) {

Log.d(TAG, “Friends loaded: “ + friends.size());

mFriendListAdatper.clear();

mFriendListAdatper.addAll(friends);

mFriendListAdatper.notifyDataSetChanged();

}

});

}

}

Using the Facebook SDK for Android to add Facebook integration to your application is significantly simpler

than using the web-based OAuth2 approach shown in the previous section. Facebook SDK for Android allows

you to provide a secure method for authorization without having to break the Android UI paradigm.

Finding Online Web Services and APIs

It’s impossible to provide a complete list for all the available web services online, mostly because that list

changes every day. However, a number of useful resources are online where you can find web services for

different things. A good place to start is at www.programmableweb.com, which provides an online directory

for many different web services and APIs. Two other very good directory resources for online web services and

APIs are www.apis.io and www.mashape.com.

If you need an online service, rest assured that you’ll be able to find some exiting web services or online APIs.

Although most of these services provide a free tier, several require you either to pay a fee (usually based on

usage or a monthly fee) or to contact the provider in order to set up a contract for their use. Often, it may be

cheaper to pay for an existing service than try to implement one from scratch, unless that’s your core business.

342 Part III: Pushing the Limits

Network and Power Consumption

When it comes to power consumption, the second biggest cause of battery drain is often related to network

traffic (the display usually comes first). Users who disable mobile data will see an immediate decrease in battery

drain, but will also lose all features requiring online connectivity. Smartphone manufacturers, chipset vendors,

and Google have all implemented a number of ways to reduce the amount of network traffic and battery drain

in order to increase the battery’s life. However, unless application developers follow the guidelines and use the

tools at their disposal, these improvements are all in vain.

Because of the openness of the Android platform, a single application can keep the mobile radio in high-drive

mode more than needed, thus draining the battery faster. In later versions of Android, users can track the

network usage for each application (see Figure 17-4). Although network traffic from your app will not cost

you anything, excessive use will cost the user through battery loss and data traffic charges, probably resulting

in a very bad review rating. It’s in your best interest to make sure you don’t use more data than is absolutely

necessary.

Figure 17-4 Data Usage from the Settings application

shows how much data each application used during a

certain period

Chapter 17: Networking, Web Service, and Remote APIs 343

The radio hardware on a smartphone, that is the Wi-Fi and the cellular chip, has built-in power-saving features

on the hardware level that automatically kicks in when no network traffic is active (that is, no incoming or

outgoing data packages) and reduces the consumption of power to a very low level. When an application

wants to send data or when an application is waiting for incoming data and a package is received, the network

hardware will disable the power saving in order to be able to send data as fast and efficiently as possible.

If only one application on a device is making network calls, there’s no problem with consumption of power. The

problem occurs when many applications want to access the network, and they’re doing so in random order.

General Guidelines

Before performing a network call, first consider whether the user really needs this data right now. For instance,

if the data your application fetches is quite small, it’s probably okay to fetch it when the user launches the

application instead of continuously updating the data at regular intervals or when your app gets a notification

(either through Google Cloud Messaging or some other push notification service). Unnecessary network

requests is the most common mistake for applications that fetch data online. In many situations, you can wait

until the user explicitly requests the data (for instance, when the application is launched).

Second, consider how much data you need to retrieve. For example, in an e-mail application, it’s usually

enough to fetch only the ten latest e-mail headers, which could be more difficult to implement, especially on

the server-side, but will save data traffic and reduce the time required for the network call to complete. Using

different types of caches (like the response cache introduced for HttpUrlConnection in Android 4/ICS) and

retrieving smaller pages of data from the service will greatly reduce your application’s network traffic.

Also, because the HttpUrlConnection class now supports transparent compressions, make sure that the

data retrieved from the server is gzip-compressed if possible—this compress/decompress feature is enabled by

default by most major public web services. Another way to optimize the amount of data is to choose a better

data format, which usually involves a balance between size optimization and how dynamic the format is. If you

can, choose a format that allows you to extend your data definition without losing backward-compatibility.

Although JSON is good enough for most situations, and also allows you to use the same backend for web

clients, the best choice in terms of size is probably Google Protocol Buffers.

Finally, my third tip is related to the first one. If you need to notify the user about a new message or some other

online information that requires the user’s attention, you cannot wait until the user starts the application to

perform the network call. In such cases, you have two choices: polling the service at regular intervals or letting

the server push the information down to the client.

Power Efficient Network Polling

Network polling has several drawbacks but is sometimes the only viable way for you to check whether there’s

something new to fetch from an online service. Fortunately, Android provides a convenient API for doing

recurring polling through the AlarmManager API.

Following is an example of a method for scheduling a recurring polling at 15-minute intervals. Note the use of

ELAPSED_REALTIME as the type of alarm:

344 Part III: Pushing the Limits

public void scheduleNetworkPolling() {

AlarmManager alarmManager = (AlarmManager)

getSystemService(ALARM_SERVICE);

int alarmType = AlarmManager.ELAPSED_REALTIME;

long interval = AlarmManager.INTERVAL_FIFTEEN_MINUTES;

long start = System.currentTimeMillis() + interval;

Intent pollIntent

= new Intent(MyNetworkService.ACTION_PERFORM_POLL);

PendingIntent pendingIntent

= PendingIntent.getService(this, 0, pollIntent, 0);

alarmManager.setInexactRepeating(alarmType,

start, interval, pendingIntent);

}

If you need to awaken the device from a suspended mode, change ELAPSED_REALTIME to ELAPSED_

REALTIME_WAKEUP. Doing so, however, causes the device to consume more battery because it will go out of

suspend mode every 15 minutes.

The way the setInexactRepeating() works is that all applications that register for a wakeup using this

service with the same interval will be awakened at the same time. Regardless of when an application registered

for a 15-minute wakeup interval, it will receive its PendingIntent at the same time the other registered

applications do.

Although this approach doesn’t consume less battery, it will reduce the device’s overall consumption of power

if all applications that need a recurring polling interval use it. This will then ensure that no network polling

happens between the 15-minute intervals.

Server-Side Push

The best solution for reducing the number of network calls is to use a server-side push. This technique allows

the server to actively notify a client that there is new data to retrieve. The server-side push notification can

come in many forms. It can be an out-of-bounds message that comes through a messaging service not directly

connected to the Internet, like SMS, or it can be a regular TCP socket with a long keep-alive.

The most obvious choice for server-side push notifications is Google Cloud Messaging, which I explain in detail

in Chapter 19. However, two other solutions are available for server-side push.

SMS Push Notifications

As I explain in Chapter 15, you can register a BroadcastReceiver to receive incoming SMS as part of the

hidden Android APIs. This solution can work as an out-of-bounds server-side push notification that wakes up

the device and notifies it that there is new data to fetch online. Although this solution has a certain monetary

cost for the service provider, it can be used for notifications that happen less frequently or when you’re able to

send SMS for free (for instance, if you’re writing an application for a telecom operator).

The following code is a slightly modified version of the example in Chapter 15. In this case, the

method processSms() will return true or false depending on whether it’s a push notification

Chapter 17: Networking, Web Service, and Remote APIs 345

SMS you’re expecting. If true, the network service starts and you call abortBroadcast() and

setResultData(null); to ensure that the SMS is not propagated to the other receivers.

public class MySmsPushNotifier extends BroadcastReceiver {

// Copied from Telephony.java

public static final String SMS_RECEIVED_ACTION

= “android.provider.Telephony.SMS_RECEIVED”;

public static final String MESSAGE_SERVICE_NUMBER = “+461234567890”;

private static final String MESSAGE_SERVICE_PREFIX = “MYSERVICE”;

public void onReceive(Context context, Intent intent) {

String action = intent.getAction();

if (SMS_RECEIVED_ACTION.equals(action)) {

Object[] messages =

(Object[]) intent.getSerializableExtra(“pdus”);

for (Object message : messages) {

byte[] messageData = (byte[]) message;

SmsMessage smsMessage =

SmsMessage.createFromPdu(messageData);

if (processSms(smsMessage)) {

Intent networkIntent

= new Intent(MyNetworkService.

ACTION_PERFORM_NETWORK_CALL);

context.startService(networkIntent);

abortBroadcast();

setResultData(null);

}

}

}

}

private boolean processSms(SmsMessage smsMessage) {

String from = smsMessage.getOriginatingAddress();

if (MESSAGE_SERVICE_NUMBER.equals(from)) {

String messageBody = smsMessage.getMessageBody();

if (messageBody.startsWith(MESSAGE_SERVICE_PREFIX)) {

return true;

}

}

return false;

}

}

In order for your receiver to take priority over the default SMS receiver, you also need to modify the priority of

the intent-filter as shown here:

<receiver android:name=”.MySmsPushNotifier”>

<intent-filter android:priority=”9999”>

<action android:name=”android.provider.Telephony.SMS_RECEIVED” />

</intent-filter>

</receiver>

346 Part III: Pushing the Limits

Web Sockets for Server-Side Push

I describe how to use Web Sockets earlier in this chapter. You can create a light-weight server-side push

notification service using Web Sockets. This method is not foolproof, and you need to adjust the timeout on the

socket on both the server and the client for this method to be efficient.

In the following example, the callbacks signal which state the push-notification socket is in:

class PushNotificationSocket extends WebSocketClient {

public PushNotificationSocket(URI serverURI) {

super(serverURI);

}

@Override

public void onOpen(ServerHandshake serverHandshake) {

// Web Socket opened - now registered for notifications

}

@Override

public void onMessage(String message) {

try {

JSONObject jsonObject = new JSONObject(message);

parseNotification(jsonObject);

} catch (JSONException e) {

Log.e(TAG, “Error parsing notification message!”, e);

}

}

@Override

public void onClose(int code, String reason, boolean remote) {

// Socket closed - reopen if this was unintentional

// due to a network timeout or similar

}

@Override

public void onError(Exception e) {

// Error, possibly due to timeout.

// Reconnect if possible.

}

}

This code is only partially complete but illustrates how Web Sockets can be used for a light-weight server-side

push notification. You also need a server that can respond to Web Socket requests. In Chapter 18, I provide an

example of such a server using the same Web Socket library used in this chapter. You can also use this library on

a server-side Java application.

Chapter 17: Networking, Web Service, and Remote APIs 347

Summary

In this chapter, I explained the best way to use a number of different APIs and libraries for doing networking

communication over HTTP. The standard HttpUrlConnection is sufficient for most situations, but if you will

do a lot of networking calls, you should take a look at the alternatives covered in this chapter.

I also discussed how to integrate three different types of web services, from the simplest ones that don’t require

any authentication to the more complicated ones that require OAuth2 or a native Android SDK. Most of the web

services you’ll encounter can fit into one of these three categories.

In the final part of this chapter, I provided some guidelines on what to consider when it comes to power

consumption and network operations. If possible, try to use Google Cloud Messaging from a server to notify

your application when new data is available. However, when server-side push is not possible, you should use

the periodic scheduling from the AlarmManager, which will help applications perform their network polling

at the same time, thus keeping the device in a suspended mode longer.

Networking is a complicated topic, and this chapter’s space doesn’t allow coverage of all options. Before

performing network calls, make sure that the network is available and listen for changes in the connectivity

using a BroadcastReceiver, as I describe in Chapter 8. If your application transmits large amounts of data,

like high-resolution photos, consider having an option to disable network communication for your application

when on a mobile network and only allow the traffic to occur over Wi-Fi. This is best done through some settings

UI, as I describe in Chapter 9. Finally, always assume that any network operation you initiate could fail and

produce a faulty or abnormal response. Implementing proper and thorough error-handling in your network

calls is very important. I highly recommend that you write automated unit tests, as I describe in Chapter 10, to

verify your code as much as possible.

Further Resources

Documentation

For the HTTP protocol: http://www.w3.org/Protocols/

Websites

SPDY specification and whitepaper: http://dev.chromium.org/spdy/spdy-whitepaper

The latest draft for HTTP/2.0, based on SPDY: http://http2.github.io/http2-spec

The OAuth Bible from Mashape: https://github.com/Mashape/mashape-oauth/blob/

master/FLOWS.md

How OAuth2 works by Aaron Parecki: http://aaronparecki.com/articles/2012/07/29/1/

oauth2-simplified

How to perform regular updates without draining the battery: http://developer.android.com/

training/efficient-downloads/regular_updates.html

How to make more energy and network efficient apps: http://developer.sonymobile.com/

knowledge-base/tutorials/android_tutorial/how-to-develop-energy-and-

network-efficient-apps-tutorial

Chapter 18

Communicating with

Remote Devices

The most common use of network communication in Android applications is when calling online web services,

which I cover in Chapter 17. However, another area of communication is between your Android device and

some other local device. The second device can be another Android smartphone, an activity tracker, a heartbeat

monitor, a wireless weather station, or simply a regular laptop. A number of different technologies on Android

support communication with these devices. You can use a USB-cable from your Android smartphone to the

device, Bluetooth Low Energy for short-range and power-efficient wireless communication, or WiFi Direct for

high-speed peer-to-peer networking.

In this chapter, I cover some of the different communication technologies supported by Android (USB, Bluetooth

Low Energy, and Wi-Fi). I continue with some more advanced solutions using the WiFi Direct technology supported

by Android, which lets you do peer-to-peer connectivity between multiple devices using high-speed Wi-Fi. Next, I

show how you can announce and discover services on a Wi-Fi (or WiFi Direct) network using the built-in discovery

service in the Android APIs. Finally, I show how you can implement your own web server in an Android application

and how to publish an API on top of it, either using RESTful web services or an asynchronous Web Socket server.

Android’s Connectivity Technologies

Most Android devices support a number of connectivity technologies. Basically, these are USB, Bluetooth, and

Wi-Fi. Furthermore, these technologies can be divided into subcategories. For instance, you can use the APIs in

Android to perform raw serial communication over USB, or you can use the Android Open Accessory Protocol

defined by Google specifically for hardware accessories targeting Android devices. The Android Open Accessory

Protocol is supported through the Accessory Development Kit (ADK).

All Android devices support the Classic Bluetooth profiles, which are suitable for more battery-intensive operations

such as audio streaming. With Android 4.3, you now have support for Bluetooth Low Energy devices and the

Bluetooth Smart technology, which enables you to write applications that can discover and communicate with

devices that support the GATT profile (like heart monitors, step counters, and other low-power accessories).

For the more data-intensive communication scenarios, you can use Wi-Fi. Android has three modes in which its

Wi-Fi can operate: infrastructure (standard Wi-Fi connected to an access point), tethering (Android device acting

as a Wi-Fi access point to other devices), and WiFi Direct. Infrastructure is what you generally use when your

device is connected to a Wi-Fi and you access the Internet. For the purpose of this chapter, just know that this

works well when all devices are connected to the same Wi-Fi network, which usually means at home, at work or

when using a public Wi-Fi somewhere. Tethering can only be activated by the user through the Network menu

in the Settings application (unless you have access to the platform certificate, as I describe in Chapter 16), which

means it is of little interest for most developers.

350 Part III: Pushing the Limits

WiFi Direct is one of the most interesting technologies for Android, because on later devices it can be active in

parallel with the infrastructure mode (that is, you can use WiFi Direct while connected to your home Wi-Fi). WiFi

Direct allows an application to set up a peer-to-peer Wi-Fi network that works without the need for a dedicated

access point. This makes WiFi Direct very attractive for network communication between devices in a more ad

hoc scenario. It also provides high-speed connectivity that cannot be achieved by routing the communication

over a server on the Internet. For instance, if you want to create a multiplayer game that doesn’t require Internet

connectivity or if you need a fast and secure way of sharing data (like photos) between two friends, WiFi Direct

is a very suitable solution.

Android USB

USB on Android is supported through the APIs found in the package android.hardware.usb. There

is also a library for supporting USB accessories available for devices running Android 2.3.4. In this chapter, I

focus on the host mode for USB communication. For information about USB accessory mode, see http://

developer.android.com/guide/topics/connectivity/usb/accessory.html.

USB is designed so that one device acts as a host to a number of other devices. Among other things, the host

can supply power to the connected devices, which is why you don’t need an extra battery for USB mice or why

you can charge your Android smartphone using the USB port on your laptop.

An Android device can also act as a USB host, allowing you to power external devices, which means you

can connect to things like card readers, finger print scanners, and other USB connected peripherals to your

Android devices.

To enable an application to communicate over USB, first you define the Activity that starts when you

connect your USB device. In the following code, you see an example of how this can look in the manifest.

Note the metadata element that defines an XML file, which acts as a filter to select which USB devices your

application will trigger on.

<activity

android:name=”.MyUsbDemo”

android:label=”@string/app_name” >

<intent-filter>

<action

android:name=”android.hardware.usb.action.USB_DEVICE_ATTACHED”

/>

</intent-filter>

<meta-data

android:name=”android.hardware.usb.action.USB_DEVICE_ATTACHED”

android:resource=”@xml/device_filter” />

</activity>

The device_filter.xml can look like the following example. In this case, it will filter on an Arduino Uno

board and start your Activity when you plug in such a device on your Android smartphone.

<resources>

<usb-device vendor-id=”9025” product-id=”67” />

</resources>

Chapter 18: Communicating with Remote Devices 351

The onResume() method shown in the following code example demonstrates how you can get the

UsbDevice instance from the Intent that will launch your Activity.

protected void onResume() {

super.onResume();

Intent intent = getIntent();

UsbDevice device = (UsbDevice) intent.

getParcelableExtra(UsbManager.EXTRA_DEVICE);

Log.d(TAG, “Found USB Device: “ + device.toString());

new Thread(new UsbCommunciation(device)).start();

}

After you have the UsbDevice object, you can start communicating with the device by opening a connection,

claiming its interface and retrieving the endpoints for reading and writing. In the following example, I continuously

write the same message to the device and read a response, using the buildTransfer() method.

private class UsbCommunciation implements Runnable {

private final UsbDevice mDevice;

UsbCommunciation(UsbDevice dev) {

mDevice = dev;

}

@Override

public void run() {

UsbDeviceConnection usbConnection

= mUsbManager.openDevice(mDevice);

if (!usbConnection.claimInterface(mDevice.getInterface(1),

true)) {

return;

}

// Arduino USB serial converter setup

usbConnection.controlTransfer(0x21, 34, 0, 0, null, 0, 0);

usbConnection.controlTransfer(0x21, 32, 0, 0,

new byte[]{(byte) 0x80, 0x25, 0x00,

0x00, 0x00, 0x00, 0x08},

7, 0);

UsbEndpoint output = null;

UsbEndpoint input = null;

UsbInterface usbInterface = mDevice.getInterface(1);

for (int i = 0; i < usbInterface.getEndpointCount(); i++) {

if (usbInterface.getEndpoint(i).getType() ==

UsbConstants.USB_ENDPOINT_XFER_BULK) {

if (usbInterface.getEndpoint(i).getDirection() ==

UsbConstants.USB_DIR_IN) {

input = usbInterface.getEndpoint(i);

}

if (usbInterface.getEndpoint(i).getDirection() ==

UsbConstants.USB_DIR_OUT) {

output = usbInterface.getEndpoint(i);

}

352 Part III: Pushing the Limits

}

}

byte[] readBuffer = new byte[MAX_MESSAGE_SIZE];

while (!mStop) {

usbConnection.bulkTransfer(output, TEST_MESSAGE,

TEST_MESSAGE.length, 0);

int read = usbConnection.bulkTransfer(input, readBuffer,

0, readBuffer.length,

0);

handleResponse(readBuffer, read);

SystemClock.sleep(1000);

}

usbConnection.close();

usbConnection.releaseInterface(usbInterface);

}

}

USB communication in Android can be handy in situations where wireless interfaces aren’t sufficient. It can

also be a simple solution for when you need to start prototyping on a new accessory where you don’t have a

working Bluetooth or Wi-Fi stack ready.

Bluetooth Low Energy

With Android 4.3, Google introduced support for Bluetooth Smart, which also includes support for Bluetooth

Low Energy (BLE) devices like heart rate monitors and activity trackers. This opens up the possibility for you to

build new and exciting features by extending your apps with support from a wide range of devices.

Figure 18-1 illustrates how the BLE services are structured. A device can have a number of services, such as

battery, heart rate monitor, and cycling speed. Each service has a number of characteristics, such as the body

location of the heart rate monitor or the number of RPMs for your bicycle. Some characteristics, such as current

time, support writing from the client (your Android device), whereas others support only reading and/or

notifications. Furthermore, each characteristic can have a number of descriptors attached to it. Descriptors work

as additional information about the characteristic they’re attached to.

To support BLE, you first need to add the BLUETOOTH and BLUETOOTH_ADMIN permissions to your

application, as shown next. You should also declare the use of the bluetooth_le feature so that your app is

available only to devices that support it.

<uses-permission android:name=”android.permission.BLUETOOTH”/>

<uses-permission android:name=”android.permission.BLUETOOTH_ADMIN”/>

<uses-feature android:name=”android.hardware.bluetooth_le”

android:required=”true”/>

Chapter 18: Communicating with Remote Devices 353

Figure 18-1 The Bluetooth Low Energy system design

Before performing a Bluetooth operation (LE or Classic), you need to verify that it is enabled. The following

method checks whether Bluetooth is enabled and, if Bluetooth isn’t enabled, displays the system dialog asking

the user to activate it.

protected void onResume() {

super.onResume();

if(!mBluetoothAdapter.isEnabled()) {

Intent enableIntent = new Intent(BluetoothAdapter.

ACTION_REQUEST_ENABLE);

startActivityForResult(enableIntent, ENABLE_REQUEST);

}

}

Next, you can start the scanning of BLE devices, as shown here:

public void doStartBtleScan() {

mLeScanCallback = new MyLeScanCallback();

BluetoothManager bluetoothManager =

(BluetoothManager) getSystemService(Context.BLUETOOTH_

SERVICE);

mBluetoothAdapter = bluetoothManager.getAdapter();

mBluetoothAdapter.startLeScan(mLeScanCallback);

}

354 Part III: Pushing the Limits

After a BLE device is found, you’ll receive a callback where you can continue and complete the connection to

the device. In the following code, I stop further scanning of devices and initiate a connection.

private class MyLeScanCallback implements BluetoothAdapter.LeScanCallback {

@Override

public void onLeScan(BluetoothDevice bluetoothDevice,

int rssi, byte[] scanRecord) {

// TODO: Verify that we have the correct device...

mBluetoothAdapter.stopLeScan(this);

mMyGattCallback = new MyGattCallback();

mGatt = bluetoothDevice.connectGatt(BtleDemo.this,

false, mMyGattCallback);

}

}

If your application will connect to a specific type of device, you can inspect the device here before connecting.

The callback method onConnectionStateChange()receives a call once the connection is made. At this

point, you can discover the services available on the remote device, as shown here:

private class MyGattCallback extends BluetoothGattCallback {

@Override

public void onConnectionStateChange(BluetoothGatt gatt,

int status, int newState) {

super.onConnectionStateChange(gatt, status, newState);

if(newState == BluetoothGatt.STATE_CONNECTED &&

status == BluetoothGatt.GATT_SUCCESS) {

Log.d(TAG, “Connected to “ + gatt.getDevice().getName());

gatt.discoverServices();

}

}

When the BLE service discovery is completed for the device, as shown in the following code example, you can

iterate over the services and its characteristics. For each characteristic, you check whether it supports reading

and notifications, and if so call the respective method.

@Override

public void onServicesDiscovered(BluetoothGatt gatt, int status) {

super.onServicesDiscovered(gatt, status);

if(status == BluetoothGatt.GATT_SUCCESS) {

List<BluetoothGattService> services = gatt.getServices();

for (BluetoothGattService service : services) {

Log.d(TAG, “Found service: “ + service.getUuid());

for (BluetoothGattCharacteristic characteristic :

service.getCharacteristics()) {

Log.d(TAG, “Found characteristic: “ +

characteristic.getUuid());

if(hasProperty(characteristic,

BluetoothGattCharacteristic.PROPERTY_READ)) {

Chapter 18: Communicating with Remote Devices 355

Log.d(TAG, “Read characteristic: “ +

characteristic.getUuid());

gatt.readCharacteristic(characteristic);

}

if(hasProperty(characteristic,

BluetoothGattCharacteristic.PROPERTY_NOTIFY)) {

Log.d(TAG, “Register notification for characteristic: “

+ characteristic.getUuid());

gatt.setCharacteristicNotification(characteristic,

true);

}

}

}

}

}

public static boolean hasProperty(BluetoothGattCharacteristic

characteristic, int property) {

int prop = characteristic.getProperties() & property;

return prop == property;

}

The read operation on a characteristic is asynchronous, so you have to read the value in the callback. The

registration for notifications will also use a callback from the same interface to notify your application of its

updates. The following code shows how you can read the signed 32-bit integer value from this characteristic,

both for asynchronous reading and for notifications.

@Override

public void onCharacteristicRead(BluetoothGatt gatt,

BluetoothGattCharacteristic characteristic, int status) {

super.onCharacteristicRead(gatt, characteristic, status);

if(status == BluetoothGatt.GATT_SUCCESS) {

Integer value =

characteristic.getIntValue(BluetoothGattCharacteristic.FORMAT_SINT32,

0);

// TODO Handle read value...

}

}

@Override

public void onCharacteristicChanged(BluetoothGatt gatt,

BluetoothGattCharacteristic characteristic) {

super.onCharacteristicChanged(gatt, characteristic);

Integer value = characteristic.

getIntValue(BluetoothGattCharacteristic.

FORMAT_SINT32, 0);

// TODO Handle value from notification...

}

356 Part III: Pushing the Limits

Be sure to refer to the comprehensive Bluetooth specifications, which you can find at the Bluetooth

Working Group website at https://developer.bluetooth.org/gatt/Pages/default.aspx.

Android Wi-Fi

Wi-Fi is the general name for the collection of technology standards managed by the Wi-Fi Alliance. WiFi Direct

is an additional technology that runs on top of the 802.11n standard. It enables devices to communicate

without the need for a dedicated access point, which makes it very similar to Bluetooth, except that it uses high-

speed Wi-Fi communication.

However, even if you have your devices on the same Wi-Fi, you still need to discover them in order to set up a

connection. Discovery means to find the IP address of the device running the service you want to use. Android

has built-in support for network discovery on both standard Wi-Fi (infrastructure) and WiFi Direct that lets

devices find services that are announced using the DNS-SD protocol.

Network Service Discovery

USB and Bluetooth provide an automatic discovery of the service you’re providing, but it’s not the same with web

services published on a Wi-Fi network. However, there are standard discovery mechanisms supported by Android

that allow you to announce your service as well as discover services on a local network. The implementation

consists of two standards: mDNS and DNS-SD. The first (mDNS) is a multicast protocol for announcing and

discovering hosts using the UDP multicast protocol. DNS-SD is a service discovery protocol for announcing and

discovering services running on remote hosts (usually limited to the local network). Both are available in Android

through the android.net.nsd package and through the NsdManager system service.

The following code will declare a service that you have on your device:

private void announceService() {

NsdManager nsdManager = (NsdManager) getSystemService(NSD_SERVICE);

NsdServiceInfo nsdServiceInfo = new NsdServiceInfo();

nsdServiceInfo.setPort(8081);

nsdServiceInfo.setServiceName(“MyService”);

nsdServiceInfo.setServiceType(“_http._tcp.”);

mRegistrationListener = new MyRegistrationListener();

nsdManager.registerService(nsdServiceInfo,

NsdManager.PROTOCOL_DNS_SD,

mRegistrationListener);

}

Note that if you skip setting a host for the NsdServiceInfo, the IP of your device on the Wi-Fi will be used.

The service type for mDNS must be of a valid type. If the service you’re announcing is implemented as a web

server (or a Web Socket), you can use the type in the preceding example. For a detailed description of the mDNS

protocol and how to construct valid service types, see http://files.dns-sd.org/draft-cheshire-

dnsext-dns-sd.txt. After you call NsdManager.registerService(), the NsdManager starts announcing your service on the local Wi-Fi and triggers the callbacks in mRegistrationListsner when the

state of your registration changes.

Chapter 18: Communicating with Remote Devices 357

When you want to discover a service, you use the same API but call NsdManager.discoverServices()

instead, as shown here:

private void discoverService() {

mDiscoveryListener = new MyDiscoveryListener();

NsdManager nsdManager = (NsdManager) getSystemService(NSD_SERVICE);

nsdManager.discoverServices(“_http._tcp.”,

NsdManager.PROTOCOL_DNS_SD,

mDiscoveryListener);

}

Note that you provide the service type as the search query and you’ll receive callbacks once something is found,

lost, or discovery started and stopped.

After a service is found, you need to resolve it to gain full information about it. To do so, you call NsdManager.

resolveService(), as shown in the following code.

@Override

public void onServiceFound(NsdServiceInfo serviceInfo) {

NsdManager nsdManager = (NsdManager) getSystemService(NSD_SERVICE);

nsdManager.resolveService(serviceInfo, mResolveListener);

}

When the resolve is finished, you receive a callback, as shown in the following code, where you can extract the

host and the port of the remote service that you’ve discovered.

@Override

public void onServiceResolved(NsdServiceInfo serviceInfo) {

mRemoteHost = serviceInfo.getHost();

mRemotePort = serviceInfo.getPort();

}

Using the network service discovery through the NsdManager allows you to communicate with devices on

the local Wi-Fi without forcing the user to enter an IP address manually. This can be very useful when you

create an application that wants to share data, like a photo album, or when setting up a local multiplayer game

between friends.

In the section “On-Device Web Services,” I show two examples of how to implement a web service running on

the device. By combining on-device web services with the mDNS features shown in this section, you can create

powerful services that work across devices with little or no user interaction.

WiFi Direct

WiFi Direct is part of the Wi-Fi Alliance 802.11 standards and allows high-speed Wi-Fi communication

between devices without the need for a dedicated access point. It’s basically a peer-to-peer protocol using

Wi-Fi technology. All Android devices running version 2.3/Gingerbread or later support WiFi Direct, but with

Android 4.1 and the introduction of the network service discovery APIs for WiFi Direct, WiFi Direct became really

interesting for application developers.

358 Part III: Pushing the Limits

On Android devices running version 4 or later, it’s usually possible to run WiFi Direct in concurrent mode,

meaning that the device can support both WiFi Direct and regular Wi-Fi simultaneously.

First, you register a BroadcastReceiver programmatically that will be notified about the connection and

peer changes on the WiFi Direct network, as shown in the following code.

protected void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.wifi_direct_services);

IntentFilter intentFilter = new IntentFilter(WifiP2pManager.

WIFI_P2P_PEERS_CHANGED_ACTION);

intentFilter.addAction(WifiP2pManager.

WIFI_P2P_CONNECTION_CHANGED_ACTION);

mReceiver = new MyWifiDirectReceiver();

registerReceiver(mReceiver, intentFilter);

}

Next, on the device that will publish a service you initialize a WiFi Direct channel, create a

WifiP2pServiceInfo that identifies your service, and add it as a local service. That’s all you need to set up

WiFi Direct on the server side. In the following code you can see a method that performs these actions.

private void announceWiFiDirectService() {

Log.d(TAG, “Setup service announcement!”);

mWifiP2pManager = (WifiP2pManager) getSystemService(WIFI_P2P_SERVICE);

HandlerThread handlerThread = new HandlerThread(TAG);

handlerThread.start();

mWFDLooper = handlerThread.getLooper();

mChannel = mWifiP2pManager.initialize(this, mWFDLooper,

new WifiP2pManager.ChannelListener() {

@Override

public void onChannelDisconnected() {

Log.d(TAG, “onChannelDisconnected!”);

mWFDLooper.quit();

}

});

Map<String, String> txtRecords = new HashMap<String, String>();

mServiceInfo = WifiP2pDnsSdServiceInfo.newInstance(SERVICE_NAME,

“_http._tcp”,

txtRecords);

mWifiP2pManager.addLocalService(mChannel, mServiceInfo,

new WifiP2pManager.ActionListener() {

@Override

public void onSuccess() {

Log.d(TAG, “Service announcing!”);

}

@Override

public void onFailure(int i) {

Log.d(TAG, “Service announcement failed: “ + i);

}

});

}

Chapter 18: Communicating with Remote Devices 359

On the device that will act as a client, you perform a similar setup, but instead of publishing a service, you tell

the WifiP2pManager that you want to listen for peer devices and also give it a WifiP2pServiceRequest

to search for. The following code shows this method.

private void discoverWiFiDirectServices() {

mWifiP2pManager = (WifiP2pManager) getSystemService(WIFI_P2P_SERVICE);

HandlerThread handlerThread = new HandlerThread(TAG);

handlerThread.start();

mWFDLooper = handlerThread.getLooper();

mChannel = mWifiP2pManager.initialize(this, mWFDLooper,

new WifiP2pManager.ChannelListener() {

@Override

public void onChannelDisconnected() {

Log.d(TAG, “onChannelDisconnected!”);

mWFDLooper.quit();

}

});

mServiceRequest = WifiP2pDnsSdServiceRequest.newInstance(“_http._tcp”);

mWifiP2pManager.addServiceRequest(mChannel, mServiceRequest, null);

mWifiP2pManager.setServiceResponseListener(mChannel, this);

mWifiP2pManager.setDnsSdResponseListeners(mChannel, this, this);

mWifiP2pManager.discoverPeers(mChannel,

new WifiP2pManager.ActionListener() {

@Override

public void onSuccess() {

Log.d(TAG, “Peer discovery started!”);

}

@Override

public void onFailure(int i) {

Log.d(TAG, “Peer discovery failed: “ + i);

}

});

mWifiP2pManager.discoverServices(mChannel,

new WifiP2pManager.ActionListener() {

@Override

public void onSuccess() {

Log.d(TAG, “Service discovery started!”);

}

@Override

public void onFailure(int i) {

Log.d(TAG, “Service discovery failed: “ + i);

}

});

}

When a service matching your WifiP2pServiceRequest is found, the following callback will be invoked:

@Override

public void onDnsSdServiceAvailable(String instanceName,

String registrationType,

WifiP2pDevice srcDevice) {

360 Part III: Pushing the Limits

Log.d(TAG, “DNS-SD Service available: “ + srcDevice);

mWifiP2pManager.clearServiceRequests(mChannel, null);

WifiP2pConfig wifiP2pConfig = new WifiP2pConfig();

wifiP2pConfig.deviceAddress = srcDevice.deviceAddress;

wifiP2pConfig.groupOwnerIntent = 0;

mWifiP2pManager.connect(mChannel, wifiP2pConfig, null);

}

In the preceding example, you cancel any further service discovery for WiFi Direct and connect to the device.

The configuration used here lets the device know that it has a very low intention to become the group owner. It

also contains the network address (specifically, the MAC address, not the IP address) of the remote device. When

the connect() method is called, the device on the other end (that is, the server device) displays a dialog

asking the user to confirm the connection (see Figure 18-2).

Figure 18-2 The confirmation dialog that appears on the

remote device when a client requests a connection

On both devices, once the connection is established, a broadcast will trigger the BroadcastReceiver that

was registered earlier. In the following code, I check whether the device is the group owner and if not, get the

InetAddress so that a TCP connection can be made to the service on the remote device:

Chapter 18: Communicating with Remote Devices 361

public class MyWifiDirectReceiver extends BroadcastReceiver {

@Override

public void onReceive(Context context, Intent intent) {

String action = intent.getAction();

if(WifiP2pManager.WIFI_P2P_CONNECTION_CHANGED_ACTION.

equals(action)

&& mWifiP2pManager != null) {

mWifiP2pManager.requestConnectionInfo(mChannel, this);

}

}

@Override

public void onConnectionInfoAvailable(WifiP2pInfo wifiP2pInfo) {

Log.d(TAG, “Group owner address: “ + wifiP2pInfo.

groupOwnerAddress);

Log.d(TAG, “Am I group owner: “ + wifiP2pInfo.isGroupOwner);

if(!wifiP2pInfo.isGroupOwner) {

connectToServer(wifiP2pInfo.groupOwnerAddress);

}

}

}

In this procedure, the tricky thing is to understand that you can get only the IP address of the group owner.

After a connection is made, you need to verify which device is the owner of the new P2P group and set up

the communication with that device. Hopefully the device that hosts the service (HTTP server or similar) will be

the group owner, but if that is not the case, you must provide the IP for the service host to the client in some

other way.

The main reason for using WiFi Direct is, of course, that you don’t need existing Wi-Fi infrastructure. Also,

because the setup doesn’t require additional PIN codes or passphrases, it’s much easier to connect devices

this way.

On-Device Web Services

When two remote devices need to communicate, one of them usually has to act as the server. (The exception

is when using UDP multicasting or similar techniques; however, because I usually propose building device-to-

device communication using an existing and familiar standard like HTTP, I don’t include that broader discussion

in this chapter.)

Communication can be either synchronous, which we normally associate with standard HTTP requests and

responses, or asynchronous, which is when you need something like Web Sockets to solve things.

The actual task for your on-device web service can be anything imaginable. A Wi-Fi–enabled camera could

upload photos to a simple RESTful web service that stores new photos in the album of your phone; the web

browser on your laptop could connect to your phone and receive notifications of incoming phone calls; and

SMS or another Android device could connect to your device in order to set up a simple chat session. Whatever

service you choose to provide, most of services can be implemented using the HTTP protocol.

362 Part III: Pushing the Limits

RESTful with Restlet

There are several options for implementing a web server in an Android application. Because the most common

use for implementing a web server is to provide a REST-based web service, I advise using a software component

that makes these kinds of operations easier. The one I usually choose is Restlet; it’s a lightweight Java-based

web server with good support for building RESTful web services. You can find more information about Restlet at

http://restlet.org.

The following is an example of using the Restlet engine for implementing a simple web service in an Android

application that will give the current location of the device:

public class RestletService extends Service

implements LocationListener {

public static final String ACTION_START_SERVER

= “com.aptl.myrestletdemo.START_SERVER”;

public static final String ACTION_STOP_SERVER

= “com.aptl.myrestletdemo.STOP_SERVER”;

private static final int SERVER_PORT = 8081;

public static final long ONE_MINUTE = 1000 * 60;

public static final float MIN_DISTANCE = 50;

private static final String TAG = “RestletService”;

private HandlerThread mLocationThread;

private Location mLocation;

private Component mServer;

public IBinder onBind(Intent intent) {

return null;

}

@Override

public int onStartCommand(Intent intent, int flags, int startId) {

String action = intent.getAction();

if(ACTION_START_SERVER.equals(action)) {

new Thread(new Runnable() {

@Override

public void run() {

initRestlet();

}

}).start();

} else if(ACTION_STOP_SERVER.equals(action)) {

if (mServer != null) {

shutdownRestlet();

}

}

return START_REDELIVER_INTENT;

}

private void initRestlet() {

try {

mLocationThread = new HandlerThread(“LocationUpdates”);

mLocationThread.start();

Chapter 18: Communicating with Remote Devices 363

LocationManager locationManager =

(LocationManager) getSystemService(LOCATION_SERVICE);

mLocation = locationManager.

getLastKnownLocation(LocationManager.GPS_PROVIDER);

Criteria criteria = new Criteria();

criteria.setAccuracy(Criteria.ACCURACY_FINE);

criteria.setCostAllowed(true);

criteria.setSpeedRequired(true);

criteria.setAltitudeRequired(true);

locationManager.requestLocationUpdates(ONE_MINUTE,

MIN_DISTANCE,

criteria,

this,

mLocationThread.getLooper());

mServer = new Component();

mServer.getServers().add(Protocol.HTTP, SERVER_PORT);

Router router = new Router(mServer.getContext()

.createChildContext());

router.attachDefault(new Restlet() {

@Override

public void handle(Request request,

Response response) {

response.setStatus(Status.CLIENT_ERROR_FORBIDDEN);

}

});

router.attach(“/location”, new LocationRestlet());

mServer.getDefaultHost().attach(router);

mServer.start();

} catch (Exception e) {

Log.e(TAG, “Error starting server.”, e);

}

}

private void shutdownRestlet() {

if (mServer != null) {

try {

mServer.stop();

} catch (Exception e) {

Log.e(TAG, “Error stopping server.”, e);

}

}

LocationManager locationManager =

(LocationManager) getSystemService(LOCATION_SERVICE);

locationManager.removeUpdates(this);

if (mLocationThread != null) {

mLocationThread.getLooper().quit();

mLocationThread = null;

}

}

@Override

364 Part III: Pushing the Limits

public void onLocationChanged(Location location) {

mLocation = location;

}

@Override

public void onStatusChanged(String s, int i, Bundle bundle) {

}

@Override

public void onProviderEnabled(String s) {

}

@Override

public void onProviderDisabled(String s) {

}

public class LocationRestlet extends Restlet {

@Override

public void handle(Request request, Response response) {

if(Method.GET.equals(request.getMethod())) {

try {

JSONObject jsonObject = new JSONObject();

jsonObject.put(“latitude”, mLocation.getLatitude());

jsonObject.put(“longitude”, mLocation.getLongitude());

jsonObject.put(“time”, mLocation.getTime());

jsonObject.put(“altitude”, mLocation.getAltitude());

jsonObject.put(“speed”, mLocation.getSpeed());

response.setStatus(Status.SUCCESS_OK);

response.setEntity(jsonObject.toString(),

MediaType.APPLICATION_JSON);

} catch (JSONException e) {

response.setStatus(Status.SERVER_ERROR_INTERNAL);

}

} else {

response.setStatus(Status.CLIENT_ERROR_BAD_REQUEST);

}

}

}

}

In the preceding code example the server port is hard coded. While this is technically okay, there

might be other applications running on the device that open the same port. Therefore, you should

have a proper error-handling for the cases where the port might be busy. Use the discovery services

described in the “Network Discovery Service” section to announce the port for your web server.

After setting up the server, you only need to provide your custom Restlet objects that respond to different

paths you register using the Router object.

Chapter 18: Communicating with Remote Devices 365

The Restlet API makes it easy to implement these kinds of RESTful web services. You can add variables to the

path you register the Restlet instance on and then retrieve the variables in the handle() method, as

shown here (note the variable in bold):

router.attach(“/contact/{contactId}”, new ContactsRestlet());

...

public class ContactsRestlet extends Restlet {

@Override

public void handle(Request request, Response response) {

String contactId = String.valueOf(request.

getAttributes().get(“contactId”));

JSONObject contact = new JSONObject();

// TODO: Read contact with contactId from provider...

response.setEntity(contact.toString(), MediaType.APPLICATION_

JSON);

}

}

Restlet is an excellent software library to use when you need to provide a simple HTTP-based interface to your

application running on a device. Combined with the network discovery service from the previous section, you

have a powerful method for publishing services on the local Wi-Fi.

Web Socket Server

Although Restlet allows you to implement RESTful services that are always synchronous, you sometimes need

a method for supporting asynchronous communication instead. As I show in Chapter 17, Web Sockets provide

an easy-to-use method for doing asynchronous communication on top of HTTP. Also, since it is a Web standard,

it allows web browsers to interact with this service as well. In this section I show how to use the server-side

part of the same Web Sockets library used in Chapter 17 in order to build an asynchronous web service for your

application.

The following is a simple implementation for a Service that handles a Web Socket server. The callback in

MyWebSocketServer can be used to register new clients connecting (and removing those that disconnect).

public class WebSocketService extends Service {

private static final String TAG = “WebSocketService”;

private Set<WebSocket> mClients;

private MessageListener mMessageListener;

private MyWebSocketServer mServer;

private LocalBinder mLocalBinder = new LocalBinder();

public IBinder onBind(Intent intent) {

return mLocalBinder;

}

366 Part III: Pushing the Limits

@Override

public void onCreate() {

super.onCreate();

mClients = Collections.synchronizedSet(new HashSet<WebSocket>());

}

@Override

public void onDestroy() {

super.onDestroy();

stopWebSocketServer();

}

public void startWebSocketServer() {

if (mServer == null) {

InetSocketAddress serverAddress = new InetSocketAddress(8081);

mServer = new MyWebSocketServer(serverAddress);

mServer.start();

}

}

public void stopWebSocketServer() {

if (mServer != null) {

try {

mServer.stop();

} catch (IOException e) {

Log.e(TAG, “Error stopping server.”, e);

} catch (InterruptedException e) {

Log.e(TAG, “Error stopping server.”, e);

}

}

}

public void sendBroadcast(String message) {

for (WebSocket client : mClients) {

client.send(message);

}

}

public void setMessageListener(MessageListener messageListener) {

mMessageListener = messageListener;

}

public interface MessageListener {

void onMessage(WebSocket client, String message);

}

class MyWebSocketServer extends WebSocketServer {

public MyWebSocketServer(InetSocketAddress address) {

super(address);

}

Chapter 18: Communicating with Remote Devices 367

@Override

public void onOpen(WebSocket webSocket,

ClientHandshake clientHandshake) {

mClients.add(webSocket);

}

@Override

public void onClose(WebSocket webSocket,

int code,

String reason,

boolean remote) {

mClients.remove(webSocket);

}

@Override

public void onMessage(WebSocket webSocket,

String message) {

if(mMessageListener != null) {

mMessageListener.onMessage(webSocket, message);

}

}

@Override

public void onError(WebSocket webSocket,

Exception e) {

webSocket.close();

mClients.remove(webSocket);

}

}

public class LocalBinder extends Binder {

public WebSocketService getService() {

return WebSocketService.this;

}

}

}

By providing a simple callback interface, you can have an Activity register for new incoming messages. This

way you can build a simple chat server that works on your local Wi-Fi (or over WiFi Direct).

Summary

In this chapter, I covered the three supported methods for communicating between devices: USB, Bluetooth,

and Wi-Fi. Although Bluetooth and Wi-Fi are wireless, USB still has some advantages because it’s a secure and

more reliable communication. Bluetooth, especially Bluetooth Low Energy, provides a very power-efficient

method for exchanging data between devices. The support for BLE is quite new in Android, but I expect it to be

fairly widespread in the months to come.

Finally, Wi-Fi provides high-speed communication using the standard TCP or UDP protocols that we’re used to

on the Internet. Android also provides convenient discovery APIs for both standard Wi-Fi and WiFi Direct.

368 Part III: Pushing the Limits

After you connect the devices on the same Wi-Fi, or in a WiFi Direct group, you can set up a web service or an

asynchronous Web Socket server to exchange data between the device running as a server and the one running

as a client.

Further Resources

Websites

Android Developer guidelines for USB communication: http://developer.android.com/guide/

topics/connectivity/usb/index.html

Android Developer guidelines for Bluetooth Low Energy: http://developer.android.com/

guide/topics/connectivity/bluetooth-le.html

The GATT specifications for Bluetooth Low Energy services: https://developer.bluetooth.org/

gatt/Pages/GATT-Specification-Documents.aspx

Android Developer guidelines for WiFi Direct: http://developer.android.com/guide/topics/

connectivity/wifip2p.html

Information about Multicast DNS: http://www.multicastdns.org

A list of different service types for DNS-SD: http://www.dns-sd.org/ServiceTypes.html

The main site for the Restlet Java Framework: http://restlet.org

The main site for the Java-based Web Socket library used in this chapter: https://github.com/

TooTallNate/Java-WebSocket

Chapter 19

Google Play Services

Google provides a wide range of online services that can be used on websites, desktop applications, or mobile

applications like Android. In Chapter 17, I discuss how to integrate online web services with your application,

but in those examples, I use third-party services outside the Google ecosystem. In this chapter, I focus on online

services managed by Google.

You now have available a set of APIs called Google Play Services that lets you integrate Google APIs with very

little effort, and I cover a some of those services in this chapter.

The new Location API covered in Chapter 13 is also part of Google Play Services. However, as the

Location API is a bit different from the ones covered here, it deserves its own chapter.

In this chapter, you find out how to get authorization to the online services such as Google Drive, services

running on the Google Cloud Platform, and the new Google Play Games APIs. You also discover how to set up

push notifications for your application using the Google Cloud Messaging service.

You can include the Google Play Services APIs in your project by adding the following to your Gradle build-file

dependencies section:

compile ‘com.google.android.gms:play-services:3.1.36’

Authorization

Before you can start using any of the Google Play Services, or any other Google services, you need to retrieve

an authorization token for the Google account of the user. To do so, you first need to fetch the account name

for the user.

The easiest way to retrieve the account name for a user is to use the following code:

public void doConnectAccounts(MenuItem menuItem) {

Intent intent = AccountPicker.newChooseAccountIntent(null, null,

new String[] {“com.google”}, false,

“Pick one of your Google accounts to connect.”,

null, null, null);

startActivityForResult(intent, ACCOUNT_REQUEST);

}

370 Part III: Pushing the Limits

Calling AccountPicker.newChooseAccountIntent()starts an Activity that returns the chosen

account. By passing “com.google” as one of the allowed account types, the picker will filter out all other

account types. If multiple accounts are available, the user will see a dialog box where he can select the one he

wants to use (see Figure 19-1).

Figure 19-1 The dialog box presented when there are

multiple Google accounts available

After the picker Activity returns with a result, you check whether the call was successful and retrieve the

selected account name, as shown in the following code:

protected void onActivityResult(int requestCode, int resultCode,

Intent data) {

super.onActivityResult(requestCode, resultCode, data);

if(requestCode == ACCOUNT_REQUEST) {

if(resultCode == RESULT_OK) {

String accountName

= data.getStringExtra(AccountManager.KEY_ACCOUNT_

NAME);

mPrefs.edit().putBoolean(PREFS_IS_AUTHORIZED, true)

.putString(PREFS_SELECTED_ACCOUNT, accountName).apply();

Chapter 19: Google Play Services 371

Log.d(TAG, “Picked account: “ + accountName);

invalidateOptionsMenu();

authorizeAccount(accountName);

} else {

Log.e(TAG, “No account picked...”);

}

}

}

In this case, you also store the account name in a SharedPreferences along with a flag indicating that the

user successfully selected an account.

Before you request an auth token, you must decide on the scope of the permissions you need for the services

you’ll use. These scopes are standard OAuth2 scopes, and you can find them for each Google API in their

respective online documentation. In this case, you request access to the user’s App Data on Google Drive (see

the section “Google Drive Application Data” later in this chapter), the user information (such as full name), and

the user’s Google+ information. The following code shows how such a String should look.

public static final String AUTH_SCOPE =

“oauth2:https://www.googleapis.com/auth/drive.appdata “ +

“https://www.googleapis.com/auth/userinfo.profile “ +

“https://www.googleapis.com/auth/plus.me”;

After you have the account name, you can fetch an auth token using the class GoogleAuthUtil. This

operation must run off the main thread because it performs network operations, so as shown in the following

code, you wrap it in an AsyncTask:

class MyAuthTokenTask extends AsyncTask<String,Void,String> {

@Override

protected String doInBackground(String... accountName) {

String authToken = null;

try {

authToken = GoogleAuthUtil.getToken(ServicesDemo.this,

accountName[0], AUTH_SCOPE);

} catch (IOException e) {

Log.e(TAG, “Error getting auth token.”, e);

} catch (UserRecoverableAuthException e) {

Log.d(TAG, “User recoverable error.”);

cancel(true);

startActivityForResult(e.getIntent(), TOKEN_REQUEST);

} catch (GoogleAuthException e) {

Log.e(TAG, “Error getting auth token.”, e);

}

return authToken;

}

@Override

protected void onPostExecute(String result) {

// Auth token acquired – start performing API requests

372 Part III: Pushing the Limits

if (result != null) {

mPrefs.edit().putString(PREFS_AUTH_TOKEN, result).apply();

}

}

}

This operation will always fail on the user’s first attempt (unless she authorized this application on another

device earlier), so you need to catch the UserRecoverableAuthException and call startActivityFor

Result() using the Intent contained in the exception. This brings up another dialog where the user is

informed about the API permissions your application has requested (not to be confused with standard Android

permissions). The following code shows how the onActivityResult() callback method looks in this case:

protected void onActivityResult(int requestCode, int resultCode, Intent

data) {

super.onActivityResult(requestCode, resultCode, data);

if(requestCode == ACCOUNT_REQUEST) { // Account picked

if(resultCode == RESULT_OK) {

String accountName

= data.getStringExtra(AccountManager.KEY_ACCOUNT_

NAME);

mPrefs.edit().putBoolean(PREFS_IS_AUTHORIZED, true)

.putString(PREFS_SELECTED_ACCOUNT, accountName).

apply();

invalidateOptionsMenu();

new MyAuthTokenTask().execute(accountName);

} else {

Log.e(TAG, “No account picked...”);

}

} else if(requestCode == TOKEN_REQUEST) { // Token requested

if(resultCode == RESULT_OK) {

// Try again...

new MyAuthTokenTask().

execute(mPrefs.getString(PREFS_SELECTED_ACCOUNT,

null));

}

}

}

Note that you modify the earlier onActivityResult() to handle recovery from errors that might

occur on the first call to GoogleAuthUtil.getToken(). All you do here is retry the execution of

MyAuthTokenTask because you should now have the right permissions.

Google Drive Application Data

Every Android user who has a Google account also has access to the Google Drive cloud storage service. This

service has a number of convenient APIs for reading and writing files and data to the user’s Google Drive

storage. With this the Google Drive API, you can store data in Google Drive that can be seen or accessed by only

one application. If you want your application to provide the capability of syncing its working data online, you’ll

find that this API is very handy.

Chapter 19: Google Play Services 373

This application data feature should not be confused with the backup features built into Android, where the

developer cannot control when files are backed up. This is also different from the Game Cloud Save API, which

is focused on saving game states across devices. You should use Google Drive and the Application Data features

for larger amounts of data that is only valid for your application—for instance, a sketching application that saves

files in a proprietary format.

As with the Location API described in Chapter 13, you need to add your application’s package name and SHA1

key string in the Google API Console, as shown in Figure 19-2. Create a new Client ID and add the information

needed. After this is done, your application (the package name you specified) will be authorized to access

Google Drive on behalf of users.

Figure 19-2 Adding a new Client ID for Google Drive API access

Before you get started with the Android code, you need to add the necessary dependencies to your Gradle

build file.

compile ‘com.google.api-client:google-api-client-android:1.16.0-rc’

compile ‘com.google.apis:google-api-services-drive:v2-rev89-1.15.0-rc’

The first line adds the generic Google Client for Android library. The second line adds the Google Drive–specific

library that you will use in this case.

Because your applications have already received an auth token from the authorization step (shown in the

previous section), you can now create a GoogleAccountCredentials that will be used by the Drive API.

374 Part III: Pushing the Limits

The returned Drive instance will be authorized to access the user’s private App Data. The following code

shows the createDriveService() that implements this:

public Drive createDriveService(accountName) {

try {

GoogleAccountCredential googleAccountCredential =

GoogleAccountCredential.usingOAuth2(this,

Arrays.asList(DriveScopes.DRIVE_APPDATA));

googleAccountCredential.setSelectedAccountName(accountNAme);

Drive.Builder builder =

new Drive.Builder(AndroidHttp.newCompatibleTransport(),

new AndroidJsonFactory(),

googleAccountCredential);

return builder.build();

} catch (IOException e) {

Log.e(TAG, “Error”, e);

} catch (GoogleAuthException e) {

Log.e(TAG, “Error”, e);

}

return null;

}

When you want to store data that is private to your application in the App Data folder in Google Drive, you simply

create a File object (note that this is not the java.io.File class, but one specific for Google Drive API) and

populate it with the relevant metadata. You provide it with the content (in this case, a ByteArrayContent)

and insert the file into Google Drive. The result will be a file that is hidden in the user interface of Google Drive.

The following code shows how to add a new file to the App Data “folder” of Google Drive:

class MyGoogleDriveAppDataTask extends AsyncTask<JSONObject, Void,

Integer> {

@Override

protected Integer doInBackground(JSONObject... jsonObjects) {

String accountName = mPrefs.getString(PREFS_SELECTED_ACCOUNT,

null);

Drive drive = createDriveService(accountName);

int insertedFiles = 0;

for (JSONObject jsonObject : jsonObjects) {

String dataString = jsonObject.toString();

String md5 = getMD5String(dataString.getBytes());

File file = new File();

file.setTitle(md5);

String mimeType = “application/json”;

file.setMimeType(mimeType);

file.setParents(Arrays.

asList(new ParentReference().setId(“appdata”)));

ByteArrayContent content

= new ByteArrayContent(mimeType,

dataString.getBytes());

try {

drive.files().insert(file,content).execute();

Chapter 19: Google Play Services 375

insertedFiles++;

} catch (IOException e) {

Log.e(TAG, “Failed to insert file with content “

+ dataString, e);

}

}

return insertedFiles;

}

private String getMD5String(byte[] data) {

MessageDigest mdEnc = null;

try {

mdEnc = MessageDigest.getInstance(“MD5”);

} catch (NoSuchAlgorithmException e) {

Log.e(TAG, “Error retrieving MD5 function!”, e);

return null;

}

mdEnc.update(data, 0, data.length);

return new BigInteger(1, mdEnc.digest()).toString(16);

}

}

The user can see the space used by your application, delete its content, and disconnect your app from her

Google Drive account through the Manage Apps menu in the Google Drive settings (see Figure 19-3).

Figure 19-3 The apps connected to the user’s Google Drive account

376 Part III: Pushing the Limits

Reading and updating files is done through the same API as just shown. Note that all of the operations require

network access, so remember to perform these operations off the main thread.

Google Cloud Endpoints

Google has gathered all its cloud-based developer resources and called it Google Cloud Platform. This is basically

an improved interface for App Engine, Compute Engine, Cloud Datastore, and all the other online services that

Google provides. Most of these services have a free tier, so getting started with building a cloud backend for

your mobile application is very easy. To get started, visit https://cloud.google.com/console and

create a new project.

The easiest way to get started with your own Cloud Platform backend is to generate a basic App Engine

backend from Android Studio (see Figure 19-4).

Figure 19-4 Generating App Engine endpoints with Android Studio

Chapter 19: Google Play Services 377

After the code generation is complete, you’ll have two new modules ending with “endpoint” and “AppEngine.”

You can now start to add new POJOs (Plain Old Java Objects) with getters and setters that can be queried,

inserted, updated, and deleted from your Android application.

The following code is an example of a simple POJO for storing tasks:

@Entity

public class TaskInfo {

@Id

private String id;

private String title;

private String content;

public TaskInfo() {

}

public TaskInfo(String id, String title, String content) {

this.id = id;

this.title = title;

this.content = content;

}

public String getId() {

return id;

}

public void setId(String id) {

this.id = id;

}

public String getTitle() {

return title;

}

public void setTitle(String title) {

this.title = title;

}

public String getContent() {

return content;

}

public void setContent(String content) {

this.content = content;

}

}

Following this example, you add this TaskInfo class to the AppEngine module in the project and then run

“Generate endpoint” followed by “Generate Client Libraries” from the Tools menu again. This creates the client-

side libraries that you can use in your application.

378 Part III: Pushing the Limits

The AsyncTask shown next is an example of how to add a new TaskInfo:

class AddTaskInfo extends AsyncTask<TaskInfo, Void, Void> {

@Override

protected Void doInBackground(TaskInfo... taskInfos) {

mTaskInfoEndpoint = CloudEndpointUtils.

updateBuilder(new Taskinfoendpoint.Builder(

AndroidHttp.newCompatibleTransport(),

new JacksonFactory(),

new HttpRequestInitializer() {

public void initialize(HttpRequest

httpRequest) {

}

})

).build();

for (TaskInfo taskInfo : taskInfos) {

try {

mTaskInfoEndpoint.insertTaskInfo(taskInfo).execute();;

} catch (IOException e) {

Log.e(TAG, “Error inserting task.”, e);

}

}

return null;

}

}

Note: Because they will perform network calls that would otherwise block the main thread, all these calls need

to happen on a dedicated thread.

The following shows how to use the same endpoint to query for all TaskInfo instances stored in your App

Engine instance:

class GetAllTaskInfo extends AsyncTask<Void, Void, List<TaskInfo>> {

@Override

protected List<TaskInfo> doInBackground(Void... voids) {

try {

mTaskInfoEndpoint = CloudEndpointUtils.

updateBuilder(new Taskinfoendpoint.Builder(

AndroidHttp.newCompatibleTransport(),

new JacksonFactory(),

new HttpRequestInitializer() {

public void initialize(HttpRequest

httpRequest) {

}

})).build();

return mTaskInfoEndpoint.listTaskInfo().execute().getItems();

} catch (IOException e) {

Log.e(TAG, “Error performing query.”, e);

cancel(true);

}

return null;

}

Chapter 19: Google Play Services 379

@Override

protected void onPostExecute(List<TaskInfo> taskInfos) {

super.onPostExecute(taskInfos);

mTaskInfoArrayAdapter.clear();

mTaskInfoArrayAdapter.addAll(taskInfos);

mTaskInfoArrayAdapter.notifyDataSetChanged();

}

}

The same approach can be used for updating and deleting existing TaskInfo objects.

Integrating a simple cloud backend can introduce some great features in your application. When the data your

application uses can be shared across all the user’s devices, including the user’s web browser, you can create

very powerful user experiences.

Google Cloud Messaging

When your application communicates with an online backend, such as a service implemented using Google

Cloud Endpoints, consider using server-side push notifications to notify the application when new data is

available. This reduces the need for devices to send unnecessary polling requests to your service, which drains

battery fast and also increases the load on your servers.

Google supports push notifications through the Google Cloud Messaging (GCM) service, which is part of

Google Play Services. Implementing GCM in your application allows you to send messages to the devices

running your application. The most common use for this is to let Android applications know that there’s new

data to fetch online; this is called a tickle and eliminates the need for doing recurring polling operations from

the application. You can also send application data with a payload of up to 4K using GCM.

To integrate GCM in your application, you must enable it in your Google API Console and set up a Server key for

your application under API Access. Also make sure that Google Cloud Messaging is enabled for your project.

GCM Client

On the client-side of your GCM integration, which is your Android application, you need to add a number of

elements to your manifest.

Here’s an example of how the manifest looks for the application with the package name gom.aptl.

myclouddemo.

<?xml version=”1.0” encoding=”utf-8”?>

<manifest xmlns:android=”http://schemas.android.com/apk/res/android”

package=”com.aptl.myclouddemo”

android:versionCode=”1”

android:versionName=”1.0”>

<uses-sdk

android:minSdkVersion=”18”

android:targetSdkVersion=”18”/>

380 Part III: Pushing the Limits

<uses-permission

android:name=”com.google.android.c2dm.permission.RECEIVE”/>

<uses-permission android:name=”android.permission.INTERNET”/>

<uses-permission android:name=”android.permission.GET_ACCOUNTS”/>

<uses-permission android:name=”android.permission.WAKE_LOCK”/>

<permission

android:name=”com.aptl.myclouddemo.permission.C2D_MESSAGE”

android:protectionLevel=”signature”/>

<uses-permission

android:name=”com.aptl.myclouddemo.permission.C2D_

MESSAGE”/>

<application

android:allowBackup=”true”

android:icon=”@drawable/ic_launcher”

android:label=”@string/app_name”

android:theme=”@style/AppTheme”>

<activity

android:name=”.CloudPlatformDemo”

android:label=”@string/app_name”>

<intent-filter>

<action android:name=”android.intent.action.MAIN”/>

<category android:name=”android.intent.category.

LAUNCHER”/>

</intent-filter>

</activity>

<receiver

android:name=”.MyGcmReceiver”

android:permission=”com.google.android.c2dm.SEND”>

<intent-filter>

<action

android:name=”com.google.android.c2dm.intent.

RECEIVE”/>

<category android:name=”com.aptl.myclouddemo”/>

</intent-filter>

</receiver>

<service android:name=”.MyGcmService”/>

</application>

</manifest>

Note that the new permission declared prevents other applications from receiving the same incoming message.

Also, the permission on the receiver tag ensures that no other application can send a fake message to your

application.

The first time your application starts, you need to call GoogleCloudMessaging.register() using the

sender ID, as shown in the following example:

class RegisterGcm extends AsyncTask<Void, Void, Void> {

@Override

protected Void doInBackground(Void... voids) {

Chapter 19: Google Play Services 381

try {

GoogleCloudMessaging cloudMessaging =

GoogleCloudMessaging.getInstance(CloudPlatformDemo.this);

String registrationID = cloudMessaging.register(SENDER_ID);

SharedPreferences preferences

= PreferenceManager

.getDefaultSharedPreferences(CloudPlatformDemo.this);

preferences.edit().

putString(PREFS_GCM_REG_ID, registrationID).

apply();

cloudMessaging.close();

} catch (IOException e) {

Log.e(TAG, “GCM Error.”, e);

}

return null;

}

}

The sender ID is the same as the project number that you can find on the Google Cloud Console. You will also

need to refresh the registration because it eventually times out. The default timeout for a GCM registration is

seven days.

Whenever your server sends a GCM message to your client, it triggers a call to the receiver registered for the

GCM messages. Following is a simple example where the message, stored as a Bundle in the Intent, is

passed to an IntentService for further processing:

public class MyGcmReceiver extends BroadcastReceiver {

public void onReceive(Context context, Intent intent) {

GoogleCloudMessaging cloudMessaging =

GoogleCloudMessaging.getInstance(context);

String messageType = cloudMessaging.getMessageType(intent);

if(GoogleCloudMessaging.MESSAGE_TYPE_MESSAGE.equals(messageType))

{

// Pass the message to the IntentService for processing...

Intent serviceIntent = new Intent(context,

MyGcmService.class);

serviceIntent.putExtras(intent.getExtras());

context.startService(serviceIntent);

}

setResultCode(Activity.RESULT_OK);

}

}

Before your server can send GCM messages to a user’s device, the application must provide its registration ID

to the server. The easiest way to do so is to use the DeviceInfoEndpoint that’s generated by the Google

Cloud Endpoint features in Android Studio.

The AsyncTask shown next illustrates how to use the generated endpoint for delivering the registration ID to

the client:

382 Part III: Pushing the Limits

class SendRegistrationId extends AsyncTask<Void, Void, Void> {

@Override

protected Void doInBackground(Void... voids) {

try {

SharedPreferences preferences

= PreferenceManager

.getDefaultSharedPreferences(CloudPlatformDemo.this);

String registrationId = preferences.

getString(PREFS_GCM_REG_ID,

null);

DeviceInfo deviceInfo = new DeviceInfo();

deviceInfo.setDeviceRegistrationID(registrationId);

deviceInfo.setTimestamp(System.currentTimeMillis());

deviceInfo.setDeviceInformation(“Device Info...”);

Deviceinfoendpoint deviceinfoendpoint =

CloudEndpointUtils.updateBuilder(

new Deviceinfoendpoint.Builder(

AndroidHttp.newCompatibleTransport(),

new JacksonFactory(),

new HttpRequestInitializer() {

public void initialize(HttpRequest

httpRequest)

{

}

})

).build();

deviceinfoendpoint.insertDeviceInfo(deviceInfo).execute();

} catch (IOException e) {

Log.e(TAG, “Error inserting device info.”, e);

}

return null;

}

}

The final part is sending the GCM message from the server to the clients whenever a new TaskInfo has been

inserted (see previous section). The easiest way to do so is to modify the method TaskInfoEndpoint.

insertTaskInfo() in the App Engine module, as shown in the following code. The added code is shown in

bold. In this case, a simple tickle message is sent, telling the client that new data is available online.

@ApiMethod(name = “insertTaskInfo”)

public TaskInfo insertTaskInfo(TaskInfo taskInfo) throws IOException {

EntityManager mgr = getEntityManager();

try {

if (containsTaskInfo(taskInfo)) {

throw new EntityExistsException(“Object already exists”);

}

mgr.persist(taskInfo);

Sender sender = new Sender(API_KEY);

CollectionResponse<DeviceInfo> deviceInfos

= endpoint.listDeviceInfo(null, 10);

Message message = new Message.Builder().

addData(“message”, “Task Inserted”).build();

Chapter 19: Google Play Services 383

for (DeviceInfo deviceInfo : deviceInfos.getItems()) {

sender.send(message, deviceInfo.getDeviceRegistrationID(), 5);

}

} finally {

mgr.close();

}

return taskInfo;

}

Google Play Game Services

At Google IO 2013, developers were introduced to the new online services for games. Google Play Game

Services let developers include social elements such as achievements and leaderboards, but also real-time

multiplayer support in their games.

In this section, I cover the use of the real-time multiplayer support. This is the most technically advanced feature

in this service, and by mastering it, you should have no problem integrating the other features as well.

The core of the real-time multiplayer consists of two basic concepts: a virtual room where the game takes place

and a number of participants. Players can be invited to a room or automatically matched (auto-matching of

random players). A room is created when the first player wishes to start a new multiplayer session. All players

in a multiplayer game are participants in the same room. One player can send invitations to other players from

their Google+ circles.

The easiest way to implement the real-time multiplayer service in your game is to add the BaseGameUtils

library project to your application, which can be found at https://github.com/playgameservices/

android-samples. Here, you’ll find the class BaseGameActivity, which you should extend instead of the standard Activity class, and the GameHelper, which contains a number of useful helper methods.

Next, you need to enable the Google Play Games Services for your game. You can either follow the instructions

found at https://developers.google.com/games/services/console/enabling, or you can sign in to the Google API Console, enable the necessary services for your project (Google+ API, Google Play

Game Services, Google Play Game Management, and Google Play App State), and create a new Client ID for

installed applications, as shown in Figure 19-2. Next, take the first part of the Client ID (the numbers, followed

by .apps.googleusercontent.com) and add that as a string resource value in your application.

Now, you add a new metadata tag inside your applications tag of your manifest, as shown next. This connects

the Google Play Games Services to your application.

<meta-data android:name=”com.google.android.gms.games.APP_ID”

android:value=”@string/app_id” />

In the UI shown at startup, you add a button for the user to log in to Google+ with your game. If possible, use

the readymade SignInButton in your XML layout, as shown here:

<com.google.android.gms.common.SignInButton

android:id=”@+id/sign_in_button”

android:layout_centerHorizontal=”true”

384 Part III: Pushing the Limits

android:layout_width=”wrap_content”

android:layout_height=”wrap_content”

android:onClick=”doSignIn”/>

In the click-callback shown next, you simply call the method beginUserInitiatedSignIn() from the

base class BaseGameActivity, and the user can sign in:

public void doSignIn(View view) {

beginUserInitiatedSignIn();

}

You’ll have two callbacks for the sign-in process, onSignInFailed() and onSignInSucceeded(), which

are used to process the result. If sign-in is successful, you can proceed by displaying a UI for inviting players or

seeing existing invitations.

In the two click-callbacks shown in the following code, you invite new players or display existing invitations by

starting the games client Activity:

public void doInvitePlayers(View view) {

Intent intent = getGamesClient().getSelectPlayersIntent(1, 3);

startActivityForResult(intent, INVITE_PLAYERS_RC, null);

}

public void doSeeInvitations(View view) {

Intent intent = getGamesClient().getInvitationInboxIntent();

startActivityForResult(intent, SEE_INVITATIONS_RC, null);

}

When inviting players, you must specify the maximum and minimum number of players for a game. These calls

open a new Activity from the Google Play Game Services framework that lets the user either pick opponents

or invite additional players.

Because there are two ways to start a multiplayer game, either through an invitation or by inviting players, you

need two different methods for creating the room for this game session. The following code shows how to

handle these two cases:

private void handlePlayersInvited(Intent intent) {

ArrayList<String> players = intent.

getStringArrayListExtra(GamesClient.EXTRA_PLAYERS);

int minAutoMatchPlayers = intent.

getIntExtra(GamesClient.EXTRA_MIN_AUTOMATCH_PLAYERS, 0);

int maxAutoMatchPlayers = intent.

getIntExtra(GamesClient.EXTRA_MAX_AUTOMATCH_PLAYERS, 0);

Bundle autoMatchCriteria = null;

if (minAutoMatchPlayers > 0 || maxAutoMatchPlayers > 0) {

autoMatchCriteria =

RoomConfig.createAutoMatchCriteria(minAutoMatchPlayers,

maxAutoMatchPlayers, 0);

}

Chapter 19: Google Play Services 385

RoomConfig.Builder roomConfigBuilder

= RoomConfig.builder(this);

roomConfigBuilder.addPlayersToInvite(players);

roomConfigBuilder.setMessageReceivedListener(this);

roomConfigBuilder.setRoomStatusUpdateListener(this);

if (autoMatchCriteria != null) {

roomConfigBuilder.setAutoMatchCriteria(autoMatchCriteria);

}

getGamesClient().createRoom(roomConfigBuilder.build());

}

private void handleInvitationResult(Intent intent) {

Bundle bundle = intent.getExtras();

if (bundle != null) {

Invitation invitation =

bundle.getParcelable(GamesClient.EXTRA_INVITATION);

if(invitation != null) {

RoomConfig.Builder roomConfigBuilder

= RoomConfig.builder(this);

roomConfigBuilder

.setInvitationIdToAccept(invitation.getInvitationId())

.setMessageReceivedListener(this)

.setRoomStatusUpdateListener(this);

getGamesClient().joinRoom(roomConfigBuilder.build());

}

}

}

The two interfaces RoomStatusUpdateListener and RoomUpdateListener provide a set of callback

methods for the different state changes of your multiplayer session. How you use these callbacks depends

completely on your game. The simplest solution is to direct all callbacks to the same handler where you

determine the state of the game, as shown in the following code:

@Override

public void onPeerJoined(Room room, List<String> arg1) {

updateRoom(room);

}

public void updateGameState(Room room) {

mParticipants = room.getParticipants();

// TODO: Implement change of game state here...

}

The first method shown here is one of the callbacks from RoomStatusUpdateListener. In this case, you

simply call updateGameState() with the Room as a parameter. In this method, you get the current list of

participants and then update the game state accordingly. Remember that a participant can disconnect from an

ongoing game at any time, so your code needs to deal with this and act accordingly.

During the game, each player will perform actions that will result in an update being sent to the other players.

386 Part III: Pushing the Limits

The following code shows how to send a real-time message to all participants in a room.

public void sendGameUpdate(byte[] data, Room room) {

getGamesClient().sendUnreliableRealTimeMessageToAll(data,

room.getRoomId());

}

Data Messaging

The messaging part of the real-time multiplayer API is very interesting. It allows you to send messages with data

between players in a Room without any additional servers or other infrastructure needed by you. Three types of

communication are supported by this API:

■ Reliable messaging: Allows you to send a message with a maximum of 1400 bytes with reliable data

delivery, integrity, and ordering. This means that all messages will arrive and in the right order. However,

because of network latency and such, there can be a significant delay between two messages. This should

be used for data that needs to be delivered but where latency is not an issue.

■ Unreliable messaging: This allows you to send messages with a maximum of 1168 bytes. There is no

guarantee of delivery or data arriving in order. Integrity of individual messages is preserved, but you need

to implement your game logic so that it doesn’t depend on every one of these messages being delivered or

if they’re delivered in the right order. These messages usually have a much lower latency than the reliable

messages and can thus be used for a real-time scenario.

■ Socket-based messaging: This enables streaming of data between players, instead of delivering distinct

messages. The socket-based messaging has the same limits as unreliable messaging, but usually has lower

latency than reliable messaging. However, this method of communication is more complex, and you should

use unreliable or reliable messaging as much as possible.

All three types of messaging are done through the GameClient class. Receiving messages is handled by

adding a RealTimeMessageReceivedListener to the Room before starting a game session.

Messaging Strategy

When using the real-time multiplayer API, your players can connect across networks. Google’s infrastructure

behind these services allows players to be on completely different networks and still be able to send messages