}
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/
How OAuth2 works by Aaron Parecki: http://aaronparecki.com/articles/2012/07/29/1/
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
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/
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/
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