/** * Copyright (c) 2014-present, Facebook, Inc. All rights reserved. * * You are hereby granted a non-exclusive, worldwide, royalty-free license to use, * copy, modify, and distribute this software in source code or binary form for use * in connection with the web services and APIs provided by Facebook. * * As with any software that integrates with the Facebook platform, your use of * this software is subject to the Facebook Developer Principles and Policies * [http://developers.facebook.com/policy/]. This copyright notice shall be * included in all copies or substantial portions of the software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ package com.facebook.internal; import com.facebook.FacebookRequestError; import org.json.JSONArray; import org.json.JSONObject; import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.Set; /** * com.facebook.internal is solely for the use of other packages within the Facebook SDK for * Android. Use of any of the classes in this package is unsupported, and they may be modified or * removed without warning at any time. */ public final class FacebookRequestErrorClassification { public static final int EC_SERVICE_UNAVAILABLE = 2; public static final int EC_APP_TOO_MANY_CALLS = 4; public static final int EC_RATE = 9; public static final int EC_USER_TOO_MANY_CALLS = 17; public static final int EC_INVALID_SESSION = 102; public static final int EC_INVALID_TOKEN = 190; public static final int EC_TOO_MANY_USER_ACTION_CALLS = 341; public static final String KEY_RECOVERY_MESSAGE = "recovery_message"; public static final String KEY_NAME = "name"; public static final String KEY_OTHER = "other"; public static final String KEY_TRANSIENT = "transient"; public static final String KEY_LOGIN_RECOVERABLE = "login_recoverable"; // Key is error code, value is the subcodes. Null subcodes means all subcodes are accepted. private final Map<Integer, Set<Integer>> otherErrors; private final Map<Integer, Set<Integer>> transientErrors; private final Map<Integer, Set<Integer>> loginRecoverableErrors; private final String otherRecoveryMessage; private final String transientRecoveryMessage; private final String loginRecoverableRecoveryMessage; private static FacebookRequestErrorClassification defaultInstance; FacebookRequestErrorClassification( Map<Integer, Set<Integer>> otherErrors, Map<Integer, Set<Integer>> transientErrors, Map<Integer, Set<Integer>> loginRecoverableErrors, String otherRecoveryMessage, String transientRecoveryMessage, String loginRecoverableRecoveryMessage) { this.otherErrors = otherErrors; this.transientErrors = transientErrors; this.loginRecoverableErrors = loginRecoverableErrors; this.otherRecoveryMessage = otherRecoveryMessage; this.transientRecoveryMessage = transientRecoveryMessage; this.loginRecoverableRecoveryMessage = loginRecoverableRecoveryMessage; } public Map<Integer, Set<Integer>> getOtherErrors() { return otherErrors; } public Map<Integer, Set<Integer>> getTransientErrors() { return transientErrors; } public Map<Integer, Set<Integer>> getLoginRecoverableErrors() { return loginRecoverableErrors; } public String getRecoveryMessage(FacebookRequestError.Category category) { switch (category) { case OTHER: return otherRecoveryMessage; case LOGIN_RECOVERABLE: return loginRecoverableRecoveryMessage; case TRANSIENT: return transientRecoveryMessage; default: return null; } } public FacebookRequestError.Category classify( int errorCode, int errorSubCode, boolean isTransient) { if (isTransient) { return FacebookRequestError.Category.TRANSIENT; } if (otherErrors != null && otherErrors.containsKey(errorCode)) { Set<Integer> subCodes = otherErrors.get(errorCode); if (subCodes == null || subCodes.contains(errorSubCode)) { return FacebookRequestError.Category.OTHER; } } if (loginRecoverableErrors != null && loginRecoverableErrors.containsKey(errorCode)) { Set<Integer> subCodes = loginRecoverableErrors.get(errorCode); if (subCodes == null || subCodes.contains(errorSubCode)) { return FacebookRequestError.Category.LOGIN_RECOVERABLE; } } if (transientErrors != null && transientErrors.containsKey(errorCode)) { Set<Integer> subCodes = transientErrors.get(errorCode); if (subCodes == null || subCodes.contains(errorSubCode)) { return FacebookRequestError.Category.TRANSIENT; } } return FacebookRequestError.Category.OTHER; } public static synchronized FacebookRequestErrorClassification getDefaultErrorClassification() { if (defaultInstance == null) { defaultInstance = getDefaultErrorClassificationImpl(); } return defaultInstance; } private static FacebookRequestErrorClassification getDefaultErrorClassificationImpl() { Map<Integer, Set<Integer>> transientErrors = new HashMap<Integer, Set<Integer>>() {{ put(EC_SERVICE_UNAVAILABLE, null); put(EC_APP_TOO_MANY_CALLS, null); put(EC_RATE, null); put(EC_USER_TOO_MANY_CALLS, null); put(EC_TOO_MANY_USER_ACTION_CALLS, null); }}; Map<Integer, Set<Integer>> loginRecoverableErrors = new HashMap<Integer, Set<Integer>>() {{ put(EC_INVALID_SESSION,null); put(EC_INVALID_TOKEN,null); }}; return new FacebookRequestErrorClassification( null, transientErrors, loginRecoverableErrors, null, null, null); } private static Map<Integer, Set<Integer>> parseJSONDefinition(JSONObject definition) { JSONArray itemsArray = definition.optJSONArray("items"); if (itemsArray.length() == 0) { return null; } Map<Integer, Set<Integer>> items = new HashMap<>(); for (int i = 0; i < itemsArray.length(); i++) { JSONObject item = itemsArray.optJSONObject(i); if (item == null) { continue; } int code = item.optInt("code"); if (code == 0) { continue; } Set<Integer> subcodes = null; JSONArray subcodesArray = item.optJSONArray("subcodes"); if (subcodesArray != null && subcodesArray.length() > 0) { subcodes = new HashSet<>(); for (int j = 0; j < subcodesArray.length(); j++) { int subCode = subcodesArray.optInt(j); if (subCode != 0) { subcodes.add(subCode); } } } items.put(code, subcodes); } return items; } public static FacebookRequestErrorClassification createFromJSON(JSONArray jsonArray) { if (jsonArray == null) { return null; } Map<Integer, Set<Integer>> otherErrors = null; Map<Integer, Set<Integer>> transientErrors = null; Map<Integer, Set<Integer>> loginRecoverableErrors = null; String otherRecoveryMessage = null; String transientRecoveryMessage = null; String loginRecoverableRecoveryMessage = null; for (int i = 0; i < jsonArray.length(); i++) { JSONObject definition = jsonArray.optJSONObject(i); if (definition == null) { continue; } String name = definition.optString(KEY_NAME); if (name == null) { continue; } if (name.equalsIgnoreCase(KEY_OTHER)) { otherRecoveryMessage = definition.optString(KEY_RECOVERY_MESSAGE, null); otherErrors = parseJSONDefinition(definition); } else if (name.equalsIgnoreCase(KEY_TRANSIENT)) { transientRecoveryMessage = definition.optString(KEY_RECOVERY_MESSAGE, null); transientErrors = parseJSONDefinition(definition); } else if (name.equalsIgnoreCase(KEY_LOGIN_RECOVERABLE)) { loginRecoverableRecoveryMessage = definition.optString(KEY_RECOVERY_MESSAGE, null); loginRecoverableErrors = parseJSONDefinition(definition); } } return new FacebookRequestErrorClassification( otherErrors, transientErrors, loginRecoverableErrors, otherRecoveryMessage, transientRecoveryMessage, loginRecoverableRecoveryMessage ); } }