001/* 002 * Copyright 2015 The AppAuth for Android Authors. All Rights Reserved. 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except 005 * in compliance with the License. You may obtain a copy of the License at 006 * 007 * http://www.apache.org/licenses/LICENSE-2.0 008 * 009 * Unless required by applicable law or agreed to in writing, software distributed under the 010 * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 011 * express or implied. See the License for the specific language governing permissions and 012 * limitations under the License. 013 */ 014 015package net.openid.appauth; 016 017import static net.openid.appauth.Preconditions.checkNotEmpty; 018import static net.openid.appauth.Preconditions.checkNotNull; 019 020import android.content.Intent; 021import android.net.Uri; 022import androidx.annotation.NonNull; 023import androidx.annotation.Nullable; 024import androidx.annotation.VisibleForTesting; 025import androidx.collection.ArrayMap; 026 027import org.json.JSONException; 028import org.json.JSONObject; 029 030import java.util.Collections; 031import java.util.Map; 032 033/** 034 * Returned as a response to OAuth2 requests if they fail. Specifically: 035 * 036 * - The {@link net.openid.appauth.AuthorizationService.TokenResponseCallback response} to 037 * {@link AuthorizationService#performTokenRequest(net.openid.appauth.TokenRequest, 038 * AuthorizationService.TokenResponseCallback) token requests}, 039 * 040 * - The {@link net.openid.appauth.AuthorizationServiceConfiguration.RetrieveConfigurationCallback 041 * response} 042 * to 043 * {@link AuthorizationServiceConfiguration#fetchFromUrl(android.net.Uri, 044 * AuthorizationServiceConfiguration.RetrieveConfigurationCallback) configuration retrieval}. 045 */ 046@SuppressWarnings({"ThrowableInstanceNeverThrown", "ThrowableResultOfMethodCallIgnored"}) 047public final class AuthorizationException extends Exception { 048 049 /** 050 * The extra string that used to store an {@link AuthorizationException} in an intent by 051 * {@link #toIntent()}. 052 */ 053 public static final String EXTRA_EXCEPTION = "net.openid.appauth.AuthorizationException"; 054 055 /** 056 * The OAuth2 parameter used to indicate the type of error during an authorization or 057 * token request. 058 * 059 * @see "The OAuth 2.0 Authorization Framework (RFC 6749), Section 4.1.2.1 060 * <https://tools.ietf.org/html/rfc6749#section-4.1.2.1>" 061 * @see "The OAuth 2.0 Authorization Framework" (RFC 6749), Section 5.2 062 * <https://tools.ietf.org/html/rfc6749#section-5.2>" 063 */ 064 public static final String PARAM_ERROR = "error"; 065 066 /** 067 * The OAuth2 parameter used to provide a human readable description of the error which 068 * occurred. 069 * 070 * @see "The OAuth 2.0 Authorization Framework (RFC 6749), Section 4.1.2.1 071 * <https://tools.ietf.org/html/rfc6749#section-4.1.2.1>" 072 * @see "The OAuth 2.0 Authorization Framework" (RFC 6749), Section 5.2 073 * <https://tools.ietf.org/html/rfc6749#section-5.2>" 074 */ 075 public static final String PARAM_ERROR_DESCRIPTION = "error_description"; 076 077 /** 078 * The OAuth2 parameter used to provide a URI to a human-readable page which describes the 079 * error. 080 * 081 * @see "The OAuth 2.0 Authorization Framework (RFC 6749), Section 4.1.2.1 082 * <https://tools.ietf.org/html/rfc6749#section-4.1.2.1>" 083 * @see "The OAuth 2.0 Authorization Framework" (RFC 6749), Section 5.2 084 * <https://tools.ietf.org/html/rfc6749#section-5.2>" 085 */ 086 public static final String PARAM_ERROR_URI = "error_uri"; 087 088 089 /** 090 * The error type used for all errors that are not specific to OAuth related responses. 091 */ 092 public static final int TYPE_GENERAL_ERROR = 0; 093 094 /** 095 * The error type for OAuth specific errors on the authorization endpoint. This error type is 096 * used when the server responds to an authorization request with an explicit OAuth error, as 097 * defined by [the OAuth2 specification, section 4.1.2.1]( 098 * https://tools.ietf.org/html/rfc6749#section-4.1.2.1). If the authorization response is 099 * invalid and not explicitly an error response, another error type will be used. 100 * 101 * @see "The OAuth 2.0 Authorization Framework (RFC 6749), Section 4.1.2.1 102 * <https://tools.ietf.org/html/rfc6749#section-4.1.2.1>" 103 */ 104 public static final int TYPE_OAUTH_AUTHORIZATION_ERROR = 1; 105 106 /** 107 * The error type for OAuth specific errors on the token endpoint. This error type is used when 108 * the server responds with HTTP 400 and an OAuth error, as defined by 109 * [the OAuth2 specification, section 5.2](https://tools.ietf.org/html/rfc6749#section-5.2). 110 * If an HTTP 400 response does not parse as an OAuth error (i.e. no 'error' field is present 111 * or the JSON is invalid), another error domain will be used. 112 * 113 * @see "The OAuth 2.0 Authorization Framework" (RFC 6749), Section 5.2 114 * <https://tools.ietf.org/html/rfc6749#section-5.2>" 115 */ 116 public static final int TYPE_OAUTH_TOKEN_ERROR = 2; 117 118 /** 119 * The error type for authorization errors encountered out of band on the resource server. 120 */ 121 public static final int TYPE_RESOURCE_SERVER_AUTHORIZATION_ERROR = 3; 122 123 /** 124 * The error type for OAuth specific errors on the registration endpoint. 125 */ 126 public static final int TYPE_OAUTH_REGISTRATION_ERROR = 4; 127 128 @VisibleForTesting 129 static final String KEY_TYPE = "type"; 130 131 @VisibleForTesting 132 static final String KEY_CODE = "code"; 133 134 @VisibleForTesting 135 static final String KEY_ERROR = "error"; 136 137 @VisibleForTesting 138 static final String KEY_ERROR_DESCRIPTION = "errorDescription"; 139 140 @VisibleForTesting 141 static final String KEY_ERROR_URI = "errorUri"; 142 143 /** 144 * Prime number multiplier used to produce a reasonable hash value distribution. 145 */ 146 private static final int HASH_MULTIPLIER = 31; 147 148 /** 149 * Error codes specific to AppAuth for Android, rather than those defined in the OAuth2 and 150 * OpenID specifications. 151 */ 152 public static final class GeneralErrors { 153 // codes in this group should be between 0-999 154 155 /** 156 * Indicates a problem parsing an OpenID Connect Service Discovery document. 157 */ 158 public static final AuthorizationException INVALID_DISCOVERY_DOCUMENT = 159 generalEx(0, "Invalid discovery document"); 160 161 /** 162 * Indicates the user manually canceled the OAuth authorization code flow. 163 */ 164 public static final AuthorizationException USER_CANCELED_AUTH_FLOW = 165 generalEx(1, "User cancelled flow"); 166 167 /** 168 * Indicates an OAuth authorization flow was programmatically cancelled. 169 */ 170 public static final AuthorizationException PROGRAM_CANCELED_AUTH_FLOW = 171 generalEx(2, "Flow cancelled programmatically"); 172 173 /** 174 * Indicates a network error occurred. 175 */ 176 public static final AuthorizationException NETWORK_ERROR = 177 generalEx(3, "Network error"); 178 179 /** 180 * Indicates a server error occurred. 181 */ 182 public static final AuthorizationException SERVER_ERROR = 183 generalEx(4, "Server error"); 184 185 /** 186 * Indicates a problem occurred deserializing JSON. 187 */ 188 public static final AuthorizationException JSON_DESERIALIZATION_ERROR = 189 generalEx(5, "JSON deserialization error"); 190 191 /** 192 * Indicates a problem occurred constructing a {@link TokenResponse token response} object 193 * from the JSON provided by the server. 194 */ 195 public static final AuthorizationException TOKEN_RESPONSE_CONSTRUCTION_ERROR = 196 generalEx(6, "Token response construction error"); 197 198 /** 199 * Indicates a problem parsing an OpenID Connect Registration Response. 200 */ 201 public static final AuthorizationException INVALID_REGISTRATION_RESPONSE = 202 generalEx(7, "Invalid registration response"); 203 204 /** 205 * Indicates that a received ID token could not be parsed 206 */ 207 public static final AuthorizationException ID_TOKEN_PARSING_ERROR = 208 generalEx(8, "Unable to parse ID Token"); 209 210 /** 211 * Indicates that a received ID token is invalid 212 */ 213 public static final AuthorizationException ID_TOKEN_VALIDATION_ERROR = 214 generalEx(9, "Invalid ID Token"); 215 } 216 217 /** 218 * Error codes related to failed authorization requests. 219 * 220 * @see "The OAuth 2.0 Authorization Framework (RFC 6749), Section 4.1.2.1 221 * <https://tools.ietf.org/html/rfc6749#section-4.1.2.1>" 222 */ 223 public static final class AuthorizationRequestErrors { 224 // codes in this group should be between 1000-1999 225 226 /** 227 * An `invalid_request` OAuth2 error response. 228 */ 229 public static final AuthorizationException INVALID_REQUEST = 230 authEx(1000, "invalid_request"); 231 232 /** 233 * An `unauthorized_client` OAuth2 error response. 234 */ 235 public static final AuthorizationException UNAUTHORIZED_CLIENT = 236 authEx(1001, "unauthorized_client"); 237 238 /** 239 * An `access_denied` OAuth2 error response. 240 */ 241 public static final AuthorizationException ACCESS_DENIED = 242 authEx(1002, "access_denied"); 243 244 /** 245 * An `unsupported_response_type` OAuth2 error response. 246 */ 247 public static final AuthorizationException UNSUPPORTED_RESPONSE_TYPE = 248 authEx(1003, "unsupported_response_type"); 249 250 /** 251 * An `invalid_scope` OAuth2 error response. 252 */ 253 public static final AuthorizationException INVALID_SCOPE = 254 authEx(1004, "invalid_scope"); 255 256 /** 257 * An `server_error` OAuth2 error response, equivalent to an HTTP 500 error code, but 258 * sent via redirect. 259 */ 260 public static final AuthorizationException SERVER_ERROR = 261 authEx(1005, "server_error"); 262 263 /** 264 * A `temporarily_unavailable` OAuth2 error response, equivalent to an HTTP 503 error 265 * code, but sent via redirect. 266 */ 267 public static final AuthorizationException TEMPORARILY_UNAVAILABLE = 268 authEx(1006, "temporarily_unavailable"); 269 270 /** 271 * An authorization error occurring on the client rather than the server. For example, 272 * due to client misconfiguration. This error should be treated as unrecoverable. 273 */ 274 public static final AuthorizationException CLIENT_ERROR = 275 authEx(1007, null); 276 277 /** 278 * Indicates an OAuth error as per RFC 6749, but the error code is not known to the 279 * AppAuth for Android library. It could be a custom error or code, or one from an 280 * OAuth extension. The {@link #error} field provides the exact error string returned by 281 * the server. 282 */ 283 public static final AuthorizationException OTHER = 284 authEx(1008, null); 285 286 /** 287 * Indicates that the response state param did not match the request state param, 288 * resulting in the response being discarded. 289 */ 290 public static final AuthorizationException STATE_MISMATCH = 291 generalEx(9, "Response state param did not match request state"); 292 293 private static final Map<String, AuthorizationException> STRING_TO_EXCEPTION = 294 exceptionMapByString( 295 INVALID_REQUEST, 296 UNAUTHORIZED_CLIENT, 297 ACCESS_DENIED, 298 UNSUPPORTED_RESPONSE_TYPE, 299 INVALID_SCOPE, 300 SERVER_ERROR, 301 TEMPORARILY_UNAVAILABLE, 302 CLIENT_ERROR, 303 OTHER); 304 305 /** 306 * Returns the matching exception type for the provided OAuth2 error string, or 307 * {@link #OTHER} if unknown. 308 */ 309 @NonNull 310 public static AuthorizationException byString(String error) { 311 AuthorizationException ex = STRING_TO_EXCEPTION.get(error); 312 if (ex != null) { 313 return ex; 314 } 315 return OTHER; 316 } 317 } 318 319 /** 320 * Error codes related to failed token requests. 321 * 322 * @see "The OAuth 2.0 Authorization Framework" (RFC 6749), Section 5.2 323 * <https://tools.ietf.org/html/rfc6749#section-5.2>" 324 */ 325 public static final class TokenRequestErrors { 326 // codes in this group should be between 2000-2999 327 328 /** 329 * An `invalid_request` OAuth2 error response. 330 */ 331 public static final AuthorizationException INVALID_REQUEST = 332 tokenEx(2000, "invalid_request"); 333 334 /** 335 * An `invalid_client` OAuth2 error response. 336 */ 337 public static final AuthorizationException INVALID_CLIENT = 338 tokenEx(2001, "invalid_client"); 339 340 /** 341 * An `invalid_grant` OAuth2 error response. 342 */ 343 public static final AuthorizationException INVALID_GRANT = 344 tokenEx(2002, "invalid_grant"); 345 346 /** 347 * An `unauthorized_client` OAuth2 error response. 348 */ 349 public static final AuthorizationException UNAUTHORIZED_CLIENT = 350 tokenEx(2003, "unauthorized_client"); 351 352 /** 353 * An `unsupported_grant_type` OAuth2 error response. 354 */ 355 public static final AuthorizationException UNSUPPORTED_GRANT_TYPE = 356 tokenEx(2004, "unsupported_grant_type"); 357 358 /** 359 * An `invalid_scope` OAuth2 error response. 360 */ 361 public static final AuthorizationException INVALID_SCOPE = 362 tokenEx(2005, "invalid_scope"); 363 364 /** 365 * An authorization error occurring on the client rather than the server. For example, 366 * due to client misconfiguration. This error should be treated as unrecoverable. 367 */ 368 public static final AuthorizationException CLIENT_ERROR = 369 tokenEx(2006, null); 370 371 /** 372 * Indicates an OAuth error as per RFC 6749, but the error code is not known to the 373 * AppAuth for Android library. It could be a custom error or code, or one from an 374 * OAuth extension. The {@link #error} field provides the exact error string returned by 375 * the server. 376 */ 377 public static final AuthorizationException OTHER = 378 tokenEx(2007, null); 379 380 private static final Map<String, AuthorizationException> STRING_TO_EXCEPTION = 381 exceptionMapByString( 382 INVALID_REQUEST, 383 INVALID_CLIENT, 384 INVALID_GRANT, 385 UNAUTHORIZED_CLIENT, 386 UNSUPPORTED_GRANT_TYPE, 387 INVALID_SCOPE, 388 CLIENT_ERROR, 389 OTHER); 390 391 /** 392 * Returns the matching exception type for the provided OAuth2 error string, or 393 * {@link #OTHER} if unknown. 394 */ 395 public static AuthorizationException byString(String error) { 396 AuthorizationException ex = STRING_TO_EXCEPTION.get(error); 397 if (ex != null) { 398 return ex; 399 } 400 return OTHER; 401 } 402 } 403 404 /** 405 * Error codes related to failed registration requests. 406 */ 407 public static final class RegistrationRequestErrors { 408 // codes in this group should be between 4000-4999 409 410 /** 411 * An `invalid_request` OAuth2 error response. 412 */ 413 public static final AuthorizationException INVALID_REQUEST = 414 registrationEx(4000, "invalid_request"); 415 416 /** 417 * An `invalid_client` OAuth2 error response. 418 */ 419 public static final AuthorizationException INVALID_REDIRECT_URI = 420 registrationEx(4001, "invalid_redirect_uri"); 421 422 /** 423 * An `invalid_grant` OAuth2 error response. 424 */ 425 public static final AuthorizationException INVALID_CLIENT_METADATA = 426 registrationEx(4002, "invalid_client_metadata"); 427 428 /** 429 * An authorization error occurring on the client rather than the server. For example, 430 * due to client misconfiguration. This error should be treated as unrecoverable. 431 */ 432 public static final AuthorizationException CLIENT_ERROR = 433 registrationEx(4003, null); 434 435 /** 436 * Indicates an OAuth error as per RFC 6749, but the error code is not known to the 437 * AppAuth for Android library. It could be a custom error or code, or one from an 438 * OAuth extension. The {@link #error} field provides the exact error string returned by 439 * the server. 440 */ 441 public static final AuthorizationException OTHER = 442 registrationEx(4004, null); 443 444 private static final Map<String, AuthorizationException> STRING_TO_EXCEPTION = 445 exceptionMapByString( 446 INVALID_REQUEST, 447 INVALID_REDIRECT_URI, 448 INVALID_CLIENT_METADATA, 449 CLIENT_ERROR, 450 OTHER); 451 452 /** 453 * Returns the matching exception type for the provided OAuth2 error string, or 454 * {@link #OTHER} if unknown. 455 */ 456 public static AuthorizationException byString(String error) { 457 AuthorizationException ex = STRING_TO_EXCEPTION.get(error); 458 if (ex != null) { 459 return ex; 460 } 461 return OTHER; 462 } 463 } 464 465 private static AuthorizationException generalEx(int code, @Nullable String errorDescription) { 466 return new AuthorizationException( 467 TYPE_GENERAL_ERROR, code, null, errorDescription, null, null); 468 } 469 470 private static AuthorizationException authEx(int code, @Nullable String error) { 471 return new AuthorizationException( 472 TYPE_OAUTH_AUTHORIZATION_ERROR, code, error, null, null, null); 473 } 474 475 private static AuthorizationException tokenEx(int code, @Nullable String error) { 476 return new AuthorizationException( 477 TYPE_OAUTH_TOKEN_ERROR, code, error, null, null, null); 478 } 479 480 private static AuthorizationException registrationEx(int code, @Nullable String error) { 481 return new AuthorizationException( 482 TYPE_OAUTH_REGISTRATION_ERROR, code, error, null, null, null); 483 } 484 485 /** 486 * Creates an exception based on one of the existing values defined in 487 * {@link GeneralErrors}, {@link AuthorizationRequestErrors} or {@link TokenRequestErrors}, 488 * providing a root cause. 489 */ 490 public static AuthorizationException fromTemplate( 491 @NonNull AuthorizationException ex, 492 @Nullable Throwable rootCause) { 493 return new AuthorizationException( 494 ex.type, 495 ex.code, 496 ex.error, 497 ex.errorDescription, 498 ex.errorUri, 499 rootCause); 500 } 501 502 /** 503 * Creates an exception based on one of the existing values defined in 504 * {@link AuthorizationRequestErrors} or {@link TokenRequestErrors}, adding information 505 * retrieved from OAuth error response. 506 */ 507 public static AuthorizationException fromOAuthTemplate( 508 @NonNull AuthorizationException ex, 509 @Nullable String errorOverride, 510 @Nullable String errorDescriptionOverride, 511 @Nullable Uri errorUriOverride) { 512 return new AuthorizationException( 513 ex.type, 514 ex.code, 515 (errorOverride != null) ? errorOverride : ex.error, 516 (errorDescriptionOverride != null) ? errorDescriptionOverride : ex.errorDescription, 517 (errorUriOverride != null) ? errorUriOverride : ex.errorUri, 518 null); 519 } 520 521 /** 522 * Creates an exception from an OAuth redirect URI that describes an authorization failure. 523 */ 524 public static AuthorizationException fromOAuthRedirect( 525 @NonNull Uri redirectUri) { 526 String error = redirectUri.getQueryParameter(PARAM_ERROR); 527 String errorDescription = redirectUri.getQueryParameter(PARAM_ERROR_DESCRIPTION); 528 String errorUri = redirectUri.getQueryParameter(PARAM_ERROR_URI); 529 AuthorizationException base = AuthorizationRequestErrors.byString(error); 530 return new AuthorizationException( 531 base.type, 532 base.code, 533 error, 534 errorDescription != null ? errorDescription : base.errorDescription, 535 errorUri != null ? Uri.parse(errorUri) : base.errorUri, 536 null); 537 } 538 539 /** 540 * Reconstructs an {@link AuthorizationException} from the JSON produced by 541 * {@link #toJsonString()}. 542 * @throws JSONException if the JSON is malformed or missing required properties 543 */ 544 public static AuthorizationException fromJson(@NonNull String jsonStr) throws JSONException { 545 checkNotEmpty(jsonStr, "jsonStr cannot be null or empty"); 546 return fromJson(new JSONObject(jsonStr)); 547 } 548 549 /** 550 * Reconstructs an {@link AuthorizationException} from the JSON produced by 551 * {@link #toJson()}. 552 * @throws JSONException if the JSON is malformed or missing required properties 553 */ 554 public static AuthorizationException fromJson(@NonNull JSONObject json) throws JSONException { 555 checkNotNull(json, "json cannot be null"); 556 return new AuthorizationException( 557 json.getInt(KEY_TYPE), 558 json.getInt(KEY_CODE), 559 JsonUtil.getStringIfDefined(json, KEY_ERROR), 560 JsonUtil.getStringIfDefined(json, KEY_ERROR_DESCRIPTION), 561 JsonUtil.getUriIfDefined(json, KEY_ERROR_URI), 562 null); 563 } 564 565 /** 566 * Extracts an {@link AuthorizationException} from an intent produced by {@link #toIntent()}. 567 * This is used to retrieve an error response in the handler registered for a call to 568 * {@link AuthorizationService#performAuthorizationRequest}. 569 */ 570 @Nullable 571 public static AuthorizationException fromIntent(Intent data) { 572 checkNotNull(data); 573 574 if (!data.hasExtra(EXTRA_EXCEPTION)) { 575 return null; 576 } 577 578 try { 579 return fromJson(data.getStringExtra(EXTRA_EXCEPTION)); 580 } catch (JSONException ex) { 581 throw new IllegalArgumentException("Intent contains malformed exception data", ex); 582 } 583 } 584 585 private static Map<String, AuthorizationException> exceptionMapByString( 586 AuthorizationException... exceptions) { 587 ArrayMap<String, AuthorizationException> map = 588 new ArrayMap<>(exceptions != null ? exceptions.length : 0); 589 590 if (exceptions != null) { 591 for (AuthorizationException ex : exceptions) { 592 if (ex.error != null) { 593 map.put(ex.error, ex); 594 } 595 } 596 } 597 598 return Collections.unmodifiableMap(map); 599 } 600 601 /** 602 * The type of the error. 603 * @see #TYPE_GENERAL_ERROR 604 * @see #TYPE_OAUTH_AUTHORIZATION_ERROR 605 * @see #TYPE_OAUTH_TOKEN_ERROR 606 * @see #TYPE_RESOURCE_SERVER_AUTHORIZATION_ERROR 607 */ 608 public final int type; 609 610 /** 611 * The error code describing the class of problem encountered from the set defined in this 612 * class. 613 */ 614 public final int code; 615 616 /** 617 * The error string as it is found in the OAuth2 protocol. 618 */ 619 @Nullable 620 public final String error; 621 622 /** 623 * The human readable error message associated with this exception, if available. 624 */ 625 @Nullable 626 public final String errorDescription; 627 628 /** 629 * A URI identifying a human-readable web page with information about this error. 630 */ 631 @Nullable 632 public final Uri errorUri; 633 634 /** 635 * Instantiates an authorization request with optional root cause information. 636 */ 637 public AuthorizationException( 638 int type, 639 int code, 640 @Nullable String error, 641 @Nullable String errorDescription, 642 @Nullable Uri errorUri, 643 @Nullable Throwable rootCause) { 644 super(errorDescription, rootCause); 645 this.type = type; 646 this.code = code; 647 this.error = error; 648 this.errorDescription = errorDescription; 649 this.errorUri = errorUri; 650 } 651 652 /** 653 * Produces a JSON representation of the authorization exception, for transmission or storage. 654 * This does not include any provided root cause. 655 */ 656 @NonNull 657 public JSONObject toJson() { 658 JSONObject json = new JSONObject(); 659 JsonUtil.put(json, KEY_TYPE, type); 660 JsonUtil.put(json, KEY_CODE, code); 661 JsonUtil.putIfNotNull(json, KEY_ERROR, error); 662 JsonUtil.putIfNotNull(json, KEY_ERROR_DESCRIPTION, errorDescription); 663 JsonUtil.putIfNotNull(json, KEY_ERROR_URI, errorUri); 664 return json; 665 } 666 667 /** 668 * Provides a JSON string representation of an authorization exception, for transmission or 669 * storage. This does not include any provided root cause. 670 */ 671 @NonNull 672 public String toJsonString() { 673 return toJson().toString(); 674 } 675 676 /** 677 * Creates an intent from this exception. Used to carry error responses to the handling activity 678 * specified in calls to {@link AuthorizationService#performAuthorizationRequest}. 679 */ 680 @NonNull 681 public Intent toIntent() { 682 Intent data = new Intent(); 683 data.putExtra(EXTRA_EXCEPTION, toJsonString()); 684 return data; 685 } 686 687 /** 688 * Exceptions are considered to be equal if their {@link #type type} and {@link #code code} 689 * are the same; all other properties are irrelevant for comparison. 690 */ 691 @Override 692 public boolean equals(Object obj) { 693 if (obj == this) { 694 return true; 695 } 696 697 if (obj == null || !(obj instanceof AuthorizationException)) { 698 return false; 699 } 700 701 AuthorizationException other = (AuthorizationException) obj; 702 return this.type == other.type && this.code == other.code; 703 } 704 705 @Override 706 public int hashCode() { 707 // equivalent to Arrays.hashCode(new int[] { type, code }); 708 return (HASH_MULTIPLIER * (HASH_MULTIPLIER + type)) + code; 709 } 710 711 @Override 712 public String toString() { 713 return "AuthorizationException: " + toJsonString(); 714 } 715}