1page.title=Authorizing with Google for REST APIs 2page.tags="oauth 2.0","GoogleAuthUtil" 3 4trainingnavtop=true 5startpage=true 6 7@jd:body 8 9 10<div id="qv-wrapper"> 11 <div id="qv"> 12 13<h2>In this document</h2> 14<ol> 15 <li><a href="#Register">Register Your App</a></li> 16 <li><a href="#AccountPicker">Invoke the Account Picker</a></li> 17 <li><a href="#AccountName">Retrieve the Account Name</a></li> 18 <li><a href="#ExtendAsyncTask">Extend AsyncTask to Get the Auth Token</a></li> 19 <li><a href="#HandleExceptions">Handle Exceptions</a></li> 20</ol> 21<h2>Try it out</h2> 22 23<div class="download-box"> 24<a href="http://developer.android.com/shareables/training/GoogleAuth.zip" 25 class="button">Download the sample app</a> 26<p class="filename">GoogleAuth.zip</p> 27</div> 28 29</div> 30</div> 31 32<p>When you want your Android app to access Google APIs using the user's Google account over 33HTTP, the <a href= 34"{@docRoot}reference/com/google/android/gms/auth/GoogleAuthUtil.html">{@code GoogleAuthUtil}</a> 35class and related APIs provide your users a secure and consistent experience for picking an 36account and retrieving an OAuth 2.0 token for your app.</p> 37 38<p>You can then use that token in your HTTP-based communications with Google API services 39that are not included in the <a href="{@docRoot}google/play-services/index.html">Google Play 40services</a> library, such as the Blogger or Translate APIs.</p> 41 42<p class="note"><strong>Note:</strong> An OAuth 2.0 token using <a href= 43"{@docRoot}reference/com/google/android/gms/auth/GoogleAuthUtil.html">{@code GoogleAuthUtil}</a> 44is required only for certain types of Google 45APIs that you need to access over HTTP. If you're instead using the <a 46href="{@docRoot}google/play-services/index.html">Google Play services library</a> to access Google 47APIs such as <a href="{@docRoot}google/play-services/plus.html">Google+</a> or <a 48href="{@docRoot}google/play-services/games.html">Play Games</a>, you don't need an OAuth 2.0 49token and you can instead access these services using the {@code GoogleApiClient}. For more 50information, read <a href="{@docRoot}google/auth/api-client.html">Accessing Google Play 51Services APIs</a>.</p> 52 53<p>To get started with <a href= 54"{@docRoot}reference/com/google/android/gms/auth/GoogleAuthUtil.html">{@code GoogleAuthUtil}</a> 55for accessing Google's REST APIs, you must set up your Android app project with the Google Play 56services library. Follow the procedures in <a href= 57"{@docRoot}google/play-services/setup.html">Setup Google Play Services SDK</a>.</p> 58 59 60 61 62<h2 id="Register">Register Your App</h2> 63 64<p>Before you can publish an app that retrieves an OAuth 2.0 token for Google REST APIs, 65you must register your Android app with the Google Cloud Console by providing your app's 66package name and the SHA1 fingerprint of the keystore with which you sign your release APK.</p> 67 68<p class="caution"><strong>Caution:</strong> While you are testing an APK that's <a 69href="{@docRoot}tools/publishing/app-signing.html#debugmode">signed with a 70debug key</a>, Google does not require that your app be registered in Google Cloud Console. However, 71your app must be registered in Google Cloud Console in order to continue working once it is 72<a href="{@docRoot}tools/publishing/app-signing.html#releasemode">signed 73with a release key</a>.</p> 74 75<p>To register your Android app with Google Cloud Console:</p> 76 77<ol> 78<li>Visit <a href="https://cloud.google.com/console" class="external-link" target="_blank" 79>Google Cloud Console</a>. 80<li>If you have an existing project to which you're adding an Android app, select the project. 81Otherwise, click <strong>Create project</strong> at the top, enter your project name and ID, 82then click <strong>Create</strong>. 83<p class="note"><strong>Note:</strong> The name you provide for the project is the name that 84appears to users in the Google Settings app in the list of <em>Connected apps</em>.</p> 85<li>In the left-side navigation, select <strong>APIs & auth</strong>. 86<li>Enable the API you'd like to use by setting the Status to <strong>ON</strong>. 87 88<li>In the left-side navigation, select <strong>Credentials</strong>. 89<li>Click <strong>Create new client ID</strong> or <strong>Create new key</strong> 90as appropriate for your app.</li> 91<li>Complete the form that appears by filling in your Android app details. 92<p>To get the SHA1 fingerprint for your app, run the following command in a terminal: 93<pre class="no-pretty-print"> 94keytool -exportcert -alias <keystore_alias> -keystore <keystore_path> -list -v 95</pre> 96<p>For example, you're using a debug-key with Eclipse, then the command looks like this:</p> 97<pre class="no-pretty-print"> 98keytool -exportcert -alias androiddebugkey-keystore ~/.android/debug.keystore -list -v 99</pre> 100<p>Then the keystore password is "android".</p> 101</li> 102<li>Click <strong>Create</strong>. 103</ol> 104 105<p>The Credentials page then provides the available credentials such as an OAuth 2.0 client ID and 106an Android Key, but you don't need these to authorize your Android users. Simply registering your 107app with the package name and SHA1 makes the Google services accessible by your app. 108 109 110<p>To acquire the OAuth 2.0 token that will grant you access to Google APIs over HTTP, you need to 111first identify the user's Google account with which you'll query the servers. For this task, the 112Google Play services library provides a convenient account picker dialog you can invoke using 113<a href="{@docRoot}reference/com/google/android/gms/common/AccountPicker.html">{@code 114AccountPicker}</a>. The result delivered to your activity from the account picker is the account 115name you'll use to request the OAuth 2.0 token in the next lesson.</p> 116 117<p class="note"><strong>Note:</strong> In order to use the APIs discussed here, you must 118include the Google Play services library with your project. If you haven't set up your project 119with the library yet, read the guide to <a 120href="{@docRoot}google/play-services/setup.html">Setup Google Play Services SDK</a>.</p> 121 122 123 124<h2 id="AccountPicker">Invoke the Account Picker</h2> 125 126<p>To open the account picker dialog that's managed by the Google Play services library, call 127{@link android.app.Activity#startActivityForResult startActivityForResult()} using an {@link 128android.content.Intent} returned by <a href= 129"{@docRoot}reference/com/google/android/gms/common/AccountPicker.html#newChooseAccountIntent(android.accounts.Account,%20java.util.ArrayList%3Candroid.accounts.Account%3E,%20java.lang.String[],%20boolean,%20java.lang.String,%20java.lang.String,%20java.lang.String[],%20android.os.Bundle)"> 130{@code AccountPicker.newChooseAccountIntent}</a>.</p> 131 132 133<p>For example:</p> 134<pre> 135static final int REQUEST_CODE_PICK_ACCOUNT = 1000; 136 137private void pickUserAccount() { 138 String[] accountTypes = new String[]{"com.google"}; 139 Intent intent = AccountPicker.newChooseAccountIntent(null, null, 140 accountTypes, false, null, null, null, null); 141 startActivityForResult(intent, REQUEST_CODE_PICK_ACCOUNT); 142} 143</pre> 144 145<p>When this code executes, a dialog appears for the user to pick an account. When the user 146selects the account, your activity receives the result in the {@link 147android.app.Activity#onActivityResult onActivityResult()} callback.</p> 148 149<p>Most apps should pass the <a href= 150"{@docRoot}reference/com/google/android/gms/common/AccountPicker.html#newChooseAccountIntent(android.accounts.Account,%20java.util.ArrayList%3Candroid.accounts.Account%3E,%20java.lang.String[],%20boolean,%20java.lang.String,%20java.lang.String,%20java.lang.String[],%20android.os.Bundle)"> 151{@code newChooseAccountIntent()}</a> method the same arguments shown in the above example, 152which indicate that:</p> 153 154 155<ul> 156<li>There is no currently selected account.</li> 157<li>There is no restricted list of accounts.</li> 158<li>The dialog should list only accounts from the "com.google" domain.</li> 159<li>Don't prompt the user to pick an account if there's only one available account (just use that 160one). However, even if only one account currently exists, the dialog may include an option for the 161user to add a new account.</li> 162<li>There is no custom title for the dialog.</li> 163<li>There is no specific auth token type required.</li> 164<li>There are no restrictions based on account features.</li> 165<li>There are no authenticator-specific options.</li> 166</ul> 167 168<p>For more details about these arguments, see the <a href= 169"{@docRoot}reference/com/google/android/gms/common/AccountPicker.html#newChooseAccountIntent(android.accounts.Account,%20java.util.ArrayList%3Candroid.accounts.Account%3E,%20java.lang.String[],%20boolean,%20java.lang.String,%20java.lang.String,%20java.lang.String[],%20android.os.Bundle)"> 170{@code newChooseAccountIntent()}</a> method documentation.</p> 171 172 173 174 175<h2 id="AccountName">Retrieve the Account Name</h2> 176 177<p>Once the user selects an account, your activity receives a call to its 178{@link android.app.Activity#onActivityResult onActivityResult()} method. The received 179{@link android.content.Intent} includes an extra for 180{@link android.accounts.AccountManager#KEY_ACCOUNT_NAME}, specifying the account name 181(an email address) you must use to acquire the OAuth 2.0 token.</p> 182 183<p>Here's an example implementation of the callback {@link android.app.Activity#onActivityResult 184onActivityResult()} that receives the selected account:</p> 185 186<pre> 187String mEmail; // Received from <a href= 188"{@docRoot}reference/com/google/android/gms/common/AccountPicker.html#newChooseAccountIntent(android.accounts.Account,%20java.util.ArrayList%3Candroid.accounts.Account%3E,%20java.lang.String[],%20boolean,%20java.lang.String,%20java.lang.String,%20java.lang.String[],%20android.os.Bundle)" 189>{@code newChooseAccountIntent()}</a>; passed to <a href= 190"{@docRoot}reference/com/google/android/gms/auth/GoogleAuthUtil.html#getToken(android.content.Context,%20java.lang.String,%20java.lang.String)">{@code getToken()}</a> 191 192@Override 193protected void onActivityResult(int requestCode, int resultCode, Intent data) { 194 if (requestCode == REQUEST_CODE_PICK_ACCOUNT) { 195 // Receiving a result from the AccountPicker 196 if (resultCode == RESULT_OK) { 197 mEmail = data.getStringExtra(AccountManager.KEY_ACCOUNT_NAME); 198 // With the account name acquired, go get the auth token 199 getUsername(); 200 } else if (resultCode == RESULT_CANCELED) { 201 // The account picker dialog closed without selecting an account. 202 // Notify users that they must pick an account to proceed. 203 Toast.makeText(this, R.string.pick_account, Toast.LENGTH_SHORT).show(); 204 } 205 } 206 // Later, more code will go here to handle the result from some exceptions... 207} 208</pre> 209 210<p>You can now pass the account name held by {@code mEmail} to <a href= 211"{@docRoot}reference/com/google/android/gms/auth/GoogleAuthUtil.html#getToken(android.content.Context,%20java.lang.String,%20java.lang.String)"> 212{@code GoogleAuthUtil.getToken()}</a> (which is what the {@code getUsername()} method 213does), but because it performs network transactions, this method should not be called from the 214UI thread. The next lesson shows how to create an {@link android.os.AsyncTask} to get the auth token 215on a separate thread.</p> 216 217 218<p>Once you have retrieved the account name for the user's Google account, you can call <a href= 219"{@docRoot}reference/com/google/android/gms/auth/GoogleAuthUtil.html#getToken(android.content.Context,%20java.lang.String,%20java.lang.String)"> 220{@code GoogleAuthUtil.getToken()}</a>, which returns the access token string required by Google API 221services.</p> 222 223 224<p>Calling this method is generally a straightforward procedure, but you must be 225aware that:</p> 226<ul> 227<li>The <a href= 228"{@docRoot}reference/com/google/android/gms/auth/GoogleAuthUtil.html#getToken(android.content.Context,%20java.lang.String,%20java.lang.String)"> 229{@code GoogleAuthUtil.getToken()}</a> method requires a network connection, so your app must 230acquire the {@link android.Manifest.permission#INTERNET} permission. You should also check whether 231the device has a network connection at runtime by querying {@link android.net.NetworkInfo}, which 232requires that your app also acquire the {@link android.Manifest.permission#ACCESS_NETWORK_STATE} 233permissions—for more details, read <a href= 234"{@docRoot}training/basics/network-ops/connecting.html">Connecting to the Network</a>.</li> 235<li>Because the <a href= 236"{@docRoot}reference/com/google/android/gms/auth/GoogleAuthUtil.html#getToken(android.content.Context,%20java.lang.String,%20java.lang.String)"> 237{@code GoogleAuthUtil.getToken()}</a> method performs a synchronous network transaction, you should 238always perform this call from a worker thread to avoid blocking your app's UI thread.</li> 239<li>As is true when performing any network transaction, you should be prepared to handle 240exceptions that may occur. There are also specific exceptions that 241<a href= 242"{@docRoot}reference/com/google/android/gms/auth/GoogleAuthUtil.html#getToken(android.content.Context,%20java.lang.String,%20java.lang.String)"> 243{@code GoogleAuthUtil.getToken()}</a> may throw, defined as <a href= 244"{@docRoot}reference/com/google/android/gms/auth/GoogleAuthException.html">{@code 245GoogleAuthException}</a> objects.</li> 246</ul> 247 248<p>This lesson shows how you can gracefully handle these concerns by performing authentication in 249an {@link android.os.AsyncTask} and providing users with the appropriate information and available 250actions during known exceptions.</p> 251 252<p class="note"><strong>Note:</strong> The code shown in this lesson, using <a href= 253"{@docRoot}reference/com/google/android/gms/auth/GoogleAuthUtil.html#getToken(android.content.Context,%20java.lang.String,%20java.lang.String)">{@code GoogleAuthUtil.getToken()}</a>, 254is appropriate when you will be requesting the OAuth token from an {@link android.app.Activity}. 255However, if you need to request the OAuth token from a {@link android.app.Service}, then you 256should instead use <a 257href="{@docRoot}reference/com/google/android/gms/auth/GoogleAuthUtil.html#getTokenWithNotification(android.content.Context, java.lang.String, java.lang.String, android.os.Bundle)">{@code 258getTokenWithNotification()}</a>. This method works the same as <a href= 259"{@docRoot}reference/com/google/android/gms/auth/GoogleAuthUtil.html#getToken(android.content.Context,%20java.lang.String,%20java.lang.String)">{@code GoogleAuthUtil.getToken()}</a>, but if an error occurs, it 260also creates an appropriate 261<a href="{@docRoot}guide/topics/ui/notifiers/notifications.html">notification</a> 262that allows the user can recover from the error. 263The sample available for download above includes code showing how to use this method instead.</p> 264 265 266<h2 id="ExtendAsyncTask">Extend AsyncTask to Get the Auth Token</h2> 267 268<p>The {@link android.os.AsyncTask} class provides a simple way to create a worker thread for jobs 269that should not run on your UI thread. This lesson focuses on how to create such a thread 270to get your auth token; for a more complete discussion about {@link android.os.AsyncTask}, 271read <a href="{@docRoot}training/articles/perf-anr.html">Keeping Your 272App Responsive</a> and the {@link android.os.AsyncTask} class reference.</p> 273 274 275<p>The {@link android.os.AsyncTask#doInBackground doInBackground()} method in your {@link 276android.os.AsyncTask} class is where you should call the <a href= 277"{@docRoot}reference/com/google/android/gms/auth/GoogleAuthUtil.html#getToken(android.content.Context,%20java.lang.String,%20java.lang.String)"> 278{@code GoogleAuthUtil.getToken()}</a> method. You can also use it to catch some of the generic 279exceptions that may occur during your network transactions.</p> 280 281<p>For example, here's part of an {@link android.os.AsyncTask} subclass that calls <a href= 282"{@docRoot}reference/com/google/android/gms/auth/GoogleAuthUtil.html#getToken(android.content.Context,%20java.lang.String,%20java.lang.String)"> 283{@code GoogleAuthUtil.getToken()}</a>:</p> 284 285<pre> 286public class GetUsernameTask extends AsyncTask<Void, Void, Void>{ 287 Activity mActivity; 288 String mScope; 289 String mEmail; 290 291 GetUsernameTask(Activity activity, String name, String scope) { 292 this.mActivity = activity; 293 this.mScope = scope; 294 this.mEmail = name; 295 } 296 297 /** 298 * Executes the asynchronous job. This runs when you call execute() 299 * on the AsyncTask instance. 300 */ 301 @Override 302 protected Void doInBackground(Void... params) { 303 try { 304 String token = fetchToken(); 305 if (token != null) { 306 // <b>Insert the good stuff here.</b> 307 // Use the token to access the user's Google data. 308 ... 309 } 310 } catch (IOException e) { 311 // The fetchToken() method handles Google-specific exceptions, 312 // so this indicates something went wrong at a higher level. 313 // TIP: Check for network connectivity before starting the AsyncTask. 314 ... 315 } 316 return null; 317 } 318 319 /** 320 * Gets an authentication token from Google and handles any 321 * GoogleAuthException that may occur. 322 */ 323 protected String fetchToken() throws IOException { 324 try { 325 <b>return GoogleAuthUtil.getToken(mActivity, mEmail, mScope);</b> 326 } catch (UserRecoverableAuthException userRecoverableException) { 327 // GooglePlayServices.apk is either old, disabled, or not present 328 // so we need to show the user some UI in the activity to recover. 329 mActivity.handleException(userRecoverableException); 330 } catch (GoogleAuthException fatalException) { 331 // Some other type of unrecoverable exception has occurred. 332 // Report and log the error as appropriate for your app. 333 ... 334 } 335 return null; 336 } 337 ... 338} 339</pre> 340 341<p>In order to call <a href= 342"{@docRoot}reference/com/google/android/gms/auth/GoogleAuthUtil.html#getToken(android.content.Context,%20java.lang.String,%20java.lang.String)"> 343{@code GoogleAuthUtil.getToken()}</a>, you must provide the app {@link android.content.Context}, 344the account name retrieved from the account picker, and the scope for your auth 345token request. The above sample code (and the attached sample) defines these 346arguments with class members that the host activity passes to the {@link android.os.AsyncTask} class constructor. For more information about setting the scope, see 347the <a href="#SpecifyingScopes">Specifying Scopes</a> section below. </p> 348 349<p class="note"><strong>Note:</strong> 350As shown by the {@code fetchToken()} method above, you must handle 351special exceptions that may occur during the <a href= 352"{@docRoot}reference/com/google/android/gms/auth/GoogleAuthUtil.html#getToken(android.content.Context,%20java.lang.String,%20java.lang.String)"> 353{@code GoogleAuthUtil.getToken()}</a> method. The next section shows how you should 354respond to these exceptions.</p> 355 356<p>Once you have an {@link android.os.AsyncTask} subclass defined, 357you can instantiate and execute an instance after you get the user's 358account name from the account picker. 359For example, back in the {@link android.app.Activity} class you can do something like this:</p> 360 361<pre> 362String mEmail; // Received from <a href= 363"{@docRoot}reference/com/google/android/gms/common/AccountPicker.html#newChooseAccountIntent(android.accounts.Account,%20java.util.ArrayList%3Candroid.accounts.Account%3E,%20java.lang.String[],%20boolean,%20java.lang.String,%20java.lang.String,%20java.lang.String[],%20android.os.Bundle)" 364>{@code newChooseAccountIntent()}</a>; passed to <a href= 365"{@docRoot}reference/com/google/android/gms/auth/GoogleAuthUtil.html#getToken(android.content.Context,%20java.lang.String,%20java.lang.String)">{@code getToken()}</a> 366private static final String SCOPE = 367 "oauth2:https://www.googleapis.com/auth/userinfo.profile"; 368 369/** 370 * Attempts to retrieve the username. 371 * If the account is not yet known, invoke the picker. Once the account is known, 372 * start an instance of the AsyncTask to get the auth token and do work with it. 373 */ 374private void getUsername() { 375 if (mEmail == null) { 376 pickUserAccount(); 377 } else { 378 if (isDeviceOnline()) { 379 <b>new GetUsernameTask(HelloActivity.this, mEmail, SCOPE).execute();</b> 380 } else { 381 Toast.makeText(this, R.string.not_online, Toast.LENGTH_LONG).show(); 382 } 383 } 384} 385</pre> 386 387<p>The {@code pickUserAccount()} method is shown in the first lesson, <a 388href="{@docRoot}training/auth-google/picking-account.html">Picking the User's Account</a>. 389 390<p>For information about how to check whether the device is currently online (as performed by 391the {@code isDeviceOnline()} method above), see the attached sample app or the 392<a href= 393"{@docRoot}training/basics/network-ops/connecting.html">Connecting to the Network</a> lesson.</p> 394 395<p>The only part left is how you should handle the exceptions that may occur when you call 396<a href= 397"{@docRoot}reference/com/google/android/gms/auth/GoogleAuthUtil.html#getToken(android.content.Context,%20java.lang.String,%20java.lang.String)"> 398{@code GoogleAuthUtil.getToken()}</a>.</p> 399 400<h3 id="SpecifyingScopes">Specifying scopes</h3> 401<p>The scope string is used to specify which Google services can be accessed by 402 an app using the requested auth token. An auth token can be associated with 403 multiple scopes.</p> 404<p>When specifying the scopes in your auth token request, prefix the 405 scope string with {@code "oauth2:"} followed by a list of one or more OAuth scope 406 values. Use a space to separate each scope value in the list. To see a list of 407 valid OAuth scope values for Google services, browse 408 the <a href="https://developers.google.com/oauthplayground/" 409 class="external-link">OAuth 2.0 Playground</a>.</p> 410<p class="note"><strong>Tip:</strong> Specify {@code "oauth2:<scope>"} 411 for a single scope. Specify 412 {@code "oauth2:<scope1> <scope2> <scopeN>"} for multiple 413 scopes (using a space to separate each scope).</p> 414<p>For example, to access the Google Books API, the scope is 415 {@code "oauth2:https://www.googleapis.com/auth/books"}. To add an additional 416 scope, say for Google+ login, your code might look like this:</p> 417<pre> 418private final static String BOOKS_API_SCOPE 419 = "https://www.googleapis.com/auth/books"; 420private fina; static String GPLUS_SCOPE 421 = "https://www.googleapis.com/auth/plus.login"; 422private final static String mScopes 423 = "oauth2:" + BOOKS_API_SCOPE + " " + GPLUS_SCOPE; 424String token = GoogleAuthUtil.getToken(mActivity, mEmail, mScopes); 425</pre> 426 427<h2 id="HandleExceptions">Handle Exceptions</h2> 428 429<p>As shown in the <code>fetchToken()</code> method above, you must catch all occurrences of <a href= 430"{@docRoot}reference/com/google/android/gms/auth/GoogleAuthException.html">{@code 431GoogleAuthException}</a> when you call <a href= 432"{@docRoot}reference/com/google/android/gms/auth/GoogleAuthUtil.html#getToken(android.content.Context,%20java.lang.String,%20java.lang.String)"> 433{@code GoogleAuthUtil.getToken()}</a>.</p> 434 435<p>To provide users information and a proper solution to issues that may occur while acquiring the 436auth token, it's important that you properly handle the following subclasses of <a href= 437"{@docRoot}reference/com/google/android/gms/auth/GoogleAuthException.html">{@code 438GoogleAuthException}</a>:</p> 439 440 441<dl> 442<dt><a href="{@docRoot}reference/com/google/android/gms/auth/UserRecoverableAuthException.html">{@code UserRecoverableAuthException}</a></dt> 443 <dd>This is an error that users can resolve through some verification. For example, users may 444 need to confirm that your app is allowed to access their Google data or they may need to re-enter 445 their account password. When you receive this exception, call <a href= 446"{@docRoot}reference/com/google/android/gms/auth/UserRecoverableAuthException.html#getIntent()">{@code 447getIntent()}</a> on the instance and pass the returned {@link android.content.Intent} to {@link 448android.app.Activity#startActivityForResult startActivityForResult()} to give users the opportunity 449to solve the problem, such as by logging in.</dd> 450 451<dt><a href="{@docRoot}reference/com/google/android/gms/auth/GooglePlayServicesAvailabilityException.html">{@code GooglePlayServicesAvailabilityException}</a></dt> 452 <dd>This is a specific type of <a 453 href="{@docRoot}reference/com/google/android/gms/auth/UserRecoverableAuthException.html">{@code 454 UserRecoverableAuthException}</a> indicating that the user's current version 455of Google Play services is outdated. Although the recommendation above for 456<a href="{@docRoot}reference/com/google/android/gms/auth/UserRecoverableAuthException.html">{@code 457 UserRecoverableAuthException}</a> also works for this exception, calling {@link 458android.app.Activity#startActivityForResult startActivityForResult()} will immediately send users 459 to Google Play Store to install an update, which may be confusing. So you should instead call <a 460 href="{@docRoot}reference/com/google/android/gms/auth/GooglePlayServicesAvailabilityException.html#getConnectionStatusCode()"> 461{@code getConnectionStatusCode()}</a> and pass the result to <a href= 462"{@docRoot}reference/com/google/android/gms/common/GooglePlayServicesUtil.html#getErrorDialog(int,%20android.app.Activity,%20int,%20android.content.DialogInterface.OnCancelListener)"> 463{@code GooglePlayServicesUtil.getErrorDialog()}</a>. This returns a {@link android.app.Dialog} 464that includes an appropriate message and a button to take users to Google Play Store so they 465can install an update.</dd> 466</dl> 467 468<p>For example, the <code>fetchToken()</code> method in the above sample code catches any 469occurrence of <a 470href="{@docRoot}reference/com/google/android/gms/auth/UserRecoverableAuthException.html">{@code 471UserRecoverableAuthException}</a> and passes it back to the activity with a method called 472{@code handleException()}. Here's what that method in the activity may look like:</p> 473 474 475<pre> 476static final int REQUEST_CODE_RECOVER_FROM_PLAY_SERVICES_ERROR = 1001; 477 478/** 479 * This method is a hook for background threads and async tasks that need to 480 * provide the user a response UI when an exception occurs. 481 */ 482public void handleException(final Exception e) { 483 // Because this call comes from the AsyncTask, we must ensure that the following 484 // code instead executes on the UI thread. 485 runOnUiThread(new Runnable() { 486 @Override 487 public void run() { 488 if (e instanceof GooglePlayServicesAvailabilityException) { 489 // The Google Play services APK is old, disabled, or not present. 490 // Show a dialog created by Google Play services that allows 491 // the user to update the APK 492 int statusCode = ((GooglePlayServicesAvailabilityException)e) 493 .getConnectionStatusCode(); 494 Dialog dialog = GooglePlayServicesUtil.getErrorDialog(statusCode, 495 HelloActivity.this, 496 REQUEST_CODE_RECOVER_FROM_PLAY_SERVICES_ERROR); 497 dialog.show(); 498 } else if (e instanceof UserRecoverableAuthException) { 499 // Unable to authenticate, such as when the user has not yet granted 500 // the app access to the account, but the user can fix this. 501 // Forward the user to an activity in Google Play services. 502 Intent intent = ((UserRecoverableAuthException)e).getIntent(); 503 startActivityForResult(intent, 504 REQUEST_CODE_RECOVER_FROM_PLAY_SERVICES_ERROR); 505 } 506 } 507 }); 508} 509</pre> 510 511<p>Notice that in both cases, the {@code REQUEST_CODE_RECOVER_FROM_PLAY_SERVICES_ERROR} 512request code is passed with the request to handle the exception with a dialog or activity. 513This way, when the user completes the appropriate action to resolve the exception, 514your {@link android.app.Activity#onActivityResult onActivityResult()} method receives an 515intent that includes this request code and you can try to acquire the auth 516token again.</p> 517 518 519<p>For example, the following code is a complete implementation of {@link 520android.app.Activity#onActivityResult onActivityResult()} that handles results for 521both the {@code REQUEST_CODE_PICK_ACCOUNT} action (shown in the previous lesson, <a 522href="{@docRoot}training/auth-google/picking-account.html">Picking the User's Account</a>) 523and the {@code REQUEST_CODE_RECOVER_FROM_PLAY_SERVICES_ERROR} action, which occurs after the user 524completes one of the actions above to resolve an exception.</p> 525 526 527<pre> 528@Override 529protected void onActivityResult(int requestCode, int resultCode, Intent data) { 530 if (requestCode == REQUEST_CODE_PICK_ACCOUNT) { 531 // Receiving a result from the AccountPicker 532 if (resultCode == RESULT_OK) { 533 mEmail = data.getStringExtra(AccountManager.KEY_ACCOUNT_NAME); 534 // With the account name acquired, go get the auth token 535 getUsername(); 536 } else if (resultCode == RESULT_CANCELED) { 537 // The account picker dialog closed without selecting an account. 538 // Notify users that they must pick an account to proceed. 539 Toast.makeText(this, R.string.pick_account, Toast.LENGTH_SHORT).show(); 540 } 541 } else if ((requestCode == REQUEST_CODE_RECOVER_FROM_AUTH_ERROR || 542 requestCode == REQUEST_CODE_RECOVER_FROM_PLAY_SERVICES_ERROR) 543 && resultCode == RESULT_OK) { 544 // Receiving a result that follows a GoogleAuthException, try auth again 545 getUsername(); 546 } 547} 548</pre> 549 550<p>For a complete set of code that acquires the OAuth token and queries a Google service 551over HTTP (including how to use <a 552href="{@docRoot}reference/com/google/android/gms/auth/GoogleAuthUtil.html#getTokenWithNotification(android.content.Context, java.lang.String, java.lang.String, android.os.Bundle)">{@code 553getTokenWithNotification()}</a> when you need to acquire the token from 554a {@link android.app.Service}), see the sample app available for download at the top 555of this page.</p> 556 557 558 559