Blame | Last modification | View Log | RSS feed
/*** Copyright 2010-present Facebook.** Licensed under the Apache License, Version 2.0 (the "License");* you may not use this file except in compliance with the License.* You may obtain a copy of the License at** http://www.apache.org/licenses/LICENSE-2.0** Unless required by applicable law or agreed to in writing, software* distributed under the License is distributed on an "AS IS" BASIS,* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.* See the License for the specific language governing permissions and* limitations under the License.*/package com.facebook.internal;import android.content.ContentResolver;import android.content.Context;import android.content.Intent;import android.content.pm.*;import android.database.Cursor;import android.net.Uri;import android.os.Build;import android.os.Bundle;import android.text.TextUtils;import com.facebook.FacebookException;import com.facebook.FacebookOperationCanceledException;import com.facebook.SessionDefaultAudience;import com.facebook.Settings;import java.util.*;/*** 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 NativeProtocol {public static final int NO_PROTOCOL_AVAILABLE = -1;private static final String FACEBOOK_PROXY_AUTH_ACTIVITY = "com.facebook.katana.ProxyAuth";private static final String FACEBOOK_TOKEN_REFRESH_ACTIVITY = "com.facebook.katana.platform.TokenRefreshService";public static final String FACEBOOK_PROXY_AUTH_PERMISSIONS_KEY = "scope";public static final String FACEBOOK_PROXY_AUTH_APP_ID_KEY = "client_id";public static final String FACEBOOK_PROXY_AUTH_E2E_KEY = "e2e";// ---------------------------------------------------------------------------------------------// Native Protocol updated 2012-11static final String INTENT_ACTION_PLATFORM_ACTIVITY = "com.facebook.platform.PLATFORM_ACTIVITY";static final String INTENT_ACTION_PLATFORM_SERVICE = "com.facebook.platform.PLATFORM_SERVICE";public static final int PROTOCOL_VERSION_20121101 = 20121101;public static final int PROTOCOL_VERSION_20130502 = 20130502;public static final int PROTOCOL_VERSION_20130618 = 20130618;public static final int PROTOCOL_VERSION_20131107 = 20131107;public static final int PROTOCOL_VERSION_20140204 = 20140204;public static final int PROTOCOL_VERSION_20140324 = 20140324;public static final int PROTOCOL_VERSION_20140701 = 20140701;public static final int PROTOCOL_VERSION_20141001 = 20141001;public static final int PROTOCOL_VERSION_20141028 = 20141028;public static final int PROTOCOL_VERSION_20141107 = 20141107; // Bucketed Result Intentspublic static final String EXTRA_PROTOCOL_VERSION = "com.facebook.platform.protocol.PROTOCOL_VERSION";public static final String EXTRA_PROTOCOL_ACTION = "com.facebook.platform.protocol.PROTOCOL_ACTION";public static final String EXTRA_PROTOCOL_CALL_ID = "com.facebook.platform.protocol.CALL_ID";public static final String EXTRA_GET_INSTALL_DATA_PACKAGE = "com.facebook.platform.extra.INSTALLDATA_PACKAGE";public static final String EXTRA_PROTOCOL_BRIDGE_ARGS ="com.facebook.platform.protocol.BRIDGE_ARGS";public static final String EXTRA_PROTOCOL_METHOD_ARGS ="com.facebook.platform.protocol.METHOD_ARGS";public static final String EXTRA_PROTOCOL_METHOD_RESULTS ="com.facebook.platform.protocol.RESULT_ARGS";public static final String BRIDGE_ARG_APP_NAME_STRING = "app_name";public static final String BRIDGE_ARG_ACTION_ID_STRING = "action_id";public static final String BRIDGE_ARG_ERROR_BUNDLE = "error";// Messages supported by PlatformService:public static final int MESSAGE_GET_ACCESS_TOKEN_REQUEST = 0x10000;public static final int MESSAGE_GET_ACCESS_TOKEN_REPLY = 0x10001;static final int MESSAGE_GET_PROTOCOL_VERSIONS_REQUEST = 0x10002;static final int MESSAGE_GET_PROTOCOL_VERSIONS_REPLY = 0x10003;public static final int MESSAGE_GET_INSTALL_DATA_REQUEST = 0x10004;public static final int MESSAGE_GET_INSTALL_DATA_REPLY = 0x10005;public static final int MESSAGE_GET_LIKE_STATUS_REQUEST = 0x10006;public static final int MESSAGE_GET_LIKE_STATUS_REPLY = 0x10007;// MESSAGE_ERROR_REPLY data keys:// See STATUS_*// MESSAGE_GET_ACCESS_TOKEN_REQUEST data keys:// EXTRA_APPLICATION_ID// MESSAGE_GET_ACCESS_TOKEN_REPLY data keys:// EXTRA_ACCESS_TOKEN// EXTRA_EXPIRES_SECONDS_SINCE_EPOCH// EXTRA_PERMISSIONS// MESSAGE_GET_LIKE_STATUS_REQUEST data keys:// EXTRA_APPLICATION_ID// EXTRA_OBJECT_ID// MESSAGE_GET_LIKE_STATUS_REPLY data keys:// EXTRA_OBJECT_IS_LIKED// EXTRA_LIKE_COUNT_STRING_WITH_LIKE// EXTRA_LIKE_COUNT_STRING_WITHOUT_LIKE// EXTRA_SOCIAL_SENTENCE_WITH_LIKE// EXTRA_SOCIAL_SENTENCE_WITHOUT_LIKE// EXTRA_UNLIKE_TOKEN// MESSAGE_GET_PROTOCOL_VERSIONS_REPLY data keys:static final String EXTRA_PROTOCOL_VERSIONS = "com.facebook.platform.extra.PROTOCOL_VERSIONS";// Values of EXTRA_PROTOCOL_ACTION supported by PlatformActivity:public static final String ACTION_FEED_DIALOG = "com.facebook.platform.action.request.FEED_DIALOG";public static final String ACTION_MESSAGE_DIALOG = "com.facebook.platform.action.request.MESSAGE_DIALOG";public static final String ACTION_OGACTIONPUBLISH_DIALOG ="com.facebook.platform.action.request.OGACTIONPUBLISH_DIALOG";public static final String ACTION_OGMESSAGEPUBLISH_DIALOG ="com.facebook.platform.action.request.OGMESSAGEPUBLISH_DIALOG";public static final String ACTION_LIKE_DIALOG ="com.facebook.platform.action.request.LIKE_DIALOG";// Values of EXTRA_PROTOCOL_ACTION values returned by PlatformActivity:public static final String ACTION_FEED_DIALOG_REPLY ="com.facebook.platform.action.reply.FEED_DIALOG";public static final String ACTION_MESSAGE_DIALOG_REPLY ="com.facebook.platform.action.reply.MESSAGE_DIALOG";public static final String ACTION_OGACTIONPUBLISH_DIALOG_REPLY ="com.facebook.platform.action.reply.OGACTIONPUBLISH_DIALOG";public static final String ACTION_OGMESSAGEPUBLISH_DIALOG_REPLY ="com.facebook.platform.action.reply.OGMESSAGEPUBLISH_DIALOG";public static final String ACTION_LIKE_DIALOG_REPLY ="com.facebook.platform.action.reply.LIKE_DIALOG";// Extras supported for ACTION_LOGIN_DIALOG:public static final String EXTRA_PERMISSIONS = "com.facebook.platform.extra.PERMISSIONS";public static final String EXTRA_APPLICATION_ID = "com.facebook.platform.extra.APPLICATION_ID";public static final String EXTRA_APPLICATION_NAME = "com.facebook.platform.extra.APPLICATION_NAME";// Extras returned by setResult() for ACTION_LOGIN_DIALOGpublic static final String EXTRA_ACCESS_TOKEN = "com.facebook.platform.extra.ACCESS_TOKEN";public static final String EXTRA_EXPIRES_SECONDS_SINCE_EPOCH ="com.facebook.platform.extra.EXPIRES_SECONDS_SINCE_EPOCH";// EXTRA_PERMISSIONSpublic static final String RESULT_ARGS_ACCESS_TOKEN = "access_token";public static final String RESULT_ARGS_EXPIRES_SECONDS_SINCE_EPOCH ="expires_seconds_since_epoch";public static final String RESULT_ARGS_PERMISSIONS = "permissions";// Extras supported for ACTION_FEED_DIALOG:public static final String EXTRA_PLACE_TAG = "com.facebook.platform.extra.PLACE";public static final String EXTRA_FRIEND_TAGS = "com.facebook.platform.extra.FRIENDS";public static final String EXTRA_LINK = "com.facebook.platform.extra.LINK";public static final String EXTRA_IMAGE = "com.facebook.platform.extra.IMAGE";public static final String EXTRA_TITLE = "com.facebook.platform.extra.TITLE";public static final String EXTRA_SUBTITLE = "com.facebook.platform.extra.SUBTITLE";public static final String EXTRA_DESCRIPTION = "com.facebook.platform.extra.DESCRIPTION";public static final String EXTRA_REF = "com.facebook.platform.extra.REF";public static final String EXTRA_DATA_FAILURES_FATAL = "com.facebook.platform.extra.DATA_FAILURES_FATAL";public static final String EXTRA_PHOTOS = "com.facebook.platform.extra.PHOTOS";public static final String METHOD_ARGS_PLACE_TAG = "PLACE";public static final String METHOD_ARGS_FRIEND_TAGS = "FRIENDS";public static final String METHOD_ARGS_LINK = "LINK";public static final String METHOD_ARGS_IMAGE = "IMAGE";public static final String METHOD_ARGS_TITLE = "TITLE";public static final String METHOD_ARGS_SUBTITLE = "SUBTITLE";public static final String METHOD_ARGS_DESCRIPTION = "DESCRIPTION";public static final String METHOD_ARGS_REF = "REF";public static final String METHOD_ARGS_DATA_FAILURES_FATAL = "DATA_FAILURES_FATAL";public static final String METHOD_ARGS_PHOTOS = "PHOTOS";public static final String METHOD_ARGS_VIDEO = "VIDEO";// Extras supported for ACTION_OGACTIONPUBLISH_DIALOG:public static final String EXTRA_ACTION = "com.facebook.platform.extra.ACTION";public static final String EXTRA_ACTION_TYPE = "com.facebook.platform.extra.ACTION_TYPE";public static final String EXTRA_PREVIEW_PROPERTY_NAME ="com.facebook.platform.extra.PREVIEW_PROPERTY_NAME";public static final String METHOD_ARGS_ACTION = "ACTION";public static final String METHOD_ARGS_ACTION_TYPE = "ACTION_TYPE";public static final String METHOD_ARGS_PREVIEW_PROPERTY_NAME = "PREVIEW_PROPERTY_NAME";// Extras supported for MESSAGE_GET_LIKE_STATUS_REQUEST:public static final String EXTRA_OBJECT_ID = "com.facebook.platform.extra.OBJECT_ID";// Extras supported in MESSAGE_GET_LIKE_STATUS_REPLY:public static final String EXTRA_OBJECT_IS_LIKED = "com.facebook.platform.extra.OBJECT_IS_LIKED";public static final String EXTRA_LIKE_COUNT_STRING_WITH_LIKE = "com.facebook.platform.extra.LIKE_COUNT_STRING_WITH_LIKE";public static final String EXTRA_LIKE_COUNT_STRING_WITHOUT_LIKE = "com.facebook.platform.extra.LIKE_COUNT_STRING_WITHOUT_LIKE";public static final String EXTRA_SOCIAL_SENTENCE_WITH_LIKE = "com.facebook.platform.extra.SOCIAL_SENTENCE_WITH_LIKE";public static final String EXTRA_SOCIAL_SENTENCE_WITHOUT_LIKE = "com.facebook.platform.extra.SOCIAL_SENTENCE_WITHOUT_LIKE";public static final String EXTRA_UNLIKE_TOKEN = "com.facebook.platform.extra.UNLIKE_TOKEN";// OG objects will have this key to set to true if they should be created as part of OG Action publishpublic static final String OPEN_GRAPH_CREATE_OBJECT_KEY = "fbsdk:create_object";// Determines whether an image is user generatedpublic static final String IMAGE_USER_GENERATED_KEY = "user_generated";// url key for imagespublic static final String IMAGE_URL_KEY = "url";// Method args supported for ACTION_LIKE_DIALOGpublic static final String METHOD_ARGS_OBJECT_ID = "object_id";// Keys for status data in MESSAGE_ERROR_REPLY from PlatformService and for error// extras returned by PlatformActivity's setResult() in case of errors:public static final String STATUS_ERROR_TYPE = "com.facebook.platform.status.ERROR_TYPE";public static final String STATUS_ERROR_DESCRIPTION ="com.facebook.platform.status.ERROR_DESCRIPTION";public static final String STATUS_ERROR_CODE = "com.facebook.platform.status.ERROR_CODE";public static final String STATUS_ERROR_SUBCODE = "com.facebook.platform.status.ERROR_SUBCODE";public static final String STATUS_ERROR_JSON = "com.facebook.platform.status.ERROR_JSON";public static final String BRIDGE_ARG_ERROR_TYPE = "error_type";public static final String BRIDGE_ARG_ERROR_DESCRIPTION = "error_description";public static final String BRIDGE_ARG_ERROR_CODE = "error_code";public static final String BRIDGE_ARG_ERROR_SUBCODE = "error_subcode";public static final String BRIDGE_ARG_ERROR_JSON = "error_json";// Expected values for ERROR_KEY_TYPE. Clients should tolerate other values:public static final String ERROR_UNKNOWN_ERROR = "UnknownError";public static final String ERROR_PROTOCOL_ERROR = "ProtocolError";public static final String ERROR_USER_CANCELED = "UserCanceled";public static final String ERROR_APPLICATION_ERROR = "ApplicationError";public static final String ERROR_NETWORK_ERROR = "NetworkError";public static final String ERROR_PERMISSION_DENIED = "PermissionDenied";public static final String ERROR_SERVICE_DISABLED = "ServiceDisabled";public static final String AUDIENCE_ME = "only_me";public static final String AUDIENCE_FRIENDS = "friends";public static final String AUDIENCE_EVERYONE = "everyone";// Request codes for different categories of native protocol calls.public static final int DIALOG_REQUEST_CODE = 0xfacf;private static final String CONTENT_SCHEME = "content://";private static final String PLATFORM_PROVIDER_VERSIONS = ".provider.PlatformProvider/versions";// Columns returned by PlatformProviderprivate static final String PLATFORM_PROVIDER_VERSION_COLUMN = "version";private static abstract class NativeAppInfo {abstract protected String getPackage();private static final String FBI_HASH = "a4b7452e2ed8f5f191058ca7bbfd26b0d3214bfc";private static final String FBL_HASH = "5e8f16062ea3cd2c4a0d547876baa6f38cabf625";private static final String FBR_HASH = "8a3c4b262d721acd49a4bf97d5213199c86fa2b9";private static final HashSet<String> validAppSignatureHashes = buildAppSignatureHashes();private static HashSet<String> buildAppSignatureHashes() {HashSet<String> set = new HashSet<String>();set.add(FBR_HASH);set.add(FBI_HASH);set.add(FBL_HASH);return set;}public boolean validateSignature(Context context, String packageName) {String brand = Build.BRAND;int applicationFlags = context.getApplicationInfo().flags;if (brand.startsWith("generic") && (applicationFlags & ApplicationInfo.FLAG_DEBUGGABLE) != 0) {// We are debugging on an emulator, don't validate package signature.return true;}PackageInfo packageInfo = null;try {packageInfo = context.getPackageManager().getPackageInfo(packageName,PackageManager.GET_SIGNATURES);} catch (PackageManager.NameNotFoundException e) {return false;}for (Signature signature : packageInfo.signatures) {String hashedSignature = Utility.sha1hash(signature.toByteArray());if (validAppSignatureHashes.contains(hashedSignature)) {return true;}}return false;}}private static class KatanaAppInfo extends NativeAppInfo {static final String KATANA_PACKAGE = "com.facebook.katana";@Overrideprotected String getPackage() {return KATANA_PACKAGE;}}private static class MessengerAppInfo extends NativeAppInfo {static final String MESSENGER_PACKAGE = "com.facebook.orca";@Overrideprotected String getPackage() {return MESSENGER_PACKAGE;}}private static class WakizashiAppInfo extends NativeAppInfo {static final String WAKIZASHI_PACKAGE = "com.facebook.wakizashi";@Overrideprotected String getPackage() {return WAKIZASHI_PACKAGE;}}private static final NativeAppInfo FACEBOOK_APP_INFO = new KatanaAppInfo();private static List<NativeAppInfo> facebookAppInfoList = buildFacebookAppList();private static Map<String, List<NativeAppInfo>> actionToAppInfoMap = buildActionToAppInfoMap();private static List<NativeAppInfo> buildFacebookAppList() {List<NativeAppInfo> list = new ArrayList<NativeAppInfo>();// Katana needs to be the first thing in the list since it will get selected as the default FACEBOOK_APP_INFOlist.add(FACEBOOK_APP_INFO);list.add(new WakizashiAppInfo());return list;}private static Map<String, List<NativeAppInfo>> buildActionToAppInfoMap() {Map<String, List<NativeAppInfo>> map = new HashMap<String, List<NativeAppInfo>>();ArrayList<NativeAppInfo> messengerAppInfoList = new ArrayList<NativeAppInfo>();messengerAppInfoList.add(new MessengerAppInfo());// Add individual actions and the list they should trymap.put(ACTION_OGACTIONPUBLISH_DIALOG, facebookAppInfoList);map.put(ACTION_FEED_DIALOG, facebookAppInfoList);map.put(ACTION_LIKE_DIALOG, facebookAppInfoList);map.put(ACTION_MESSAGE_DIALOG, messengerAppInfoList);map.put(ACTION_OGMESSAGEPUBLISH_DIALOG, messengerAppInfoList);return map;}static Intent validateActivityIntent(Context context, Intent intent, NativeAppInfo appInfo) {if (intent == null) {return null;}ResolveInfo resolveInfo = context.getPackageManager().resolveActivity(intent, 0);if (resolveInfo == null) {return null;}if (!appInfo.validateSignature(context, resolveInfo.activityInfo.packageName)) {return null;}return intent;}static Intent validateServiceIntent(Context context, Intent intent, NativeAppInfo appInfo) {if (intent == null) {return null;}ResolveInfo resolveInfo = context.getPackageManager().resolveService(intent, 0);if (resolveInfo == null) {return null;}if (!appInfo.validateSignature(context, resolveInfo.serviceInfo.packageName)) {return null;}return intent;}public static Intent createProxyAuthIntent(Context context, String applicationId, List<String> permissions,String e2e, boolean isRerequest, SessionDefaultAudience defaultAudience) {for (NativeAppInfo appInfo : facebookAppInfoList) {Intent intent = new Intent().setClassName(appInfo.getPackage(), FACEBOOK_PROXY_AUTH_ACTIVITY).putExtra(FACEBOOK_PROXY_AUTH_APP_ID_KEY, applicationId);if (!Utility.isNullOrEmpty(permissions)) {intent.putExtra(FACEBOOK_PROXY_AUTH_PERMISSIONS_KEY, TextUtils.join(",", permissions));}if (!Utility.isNullOrEmpty(e2e)) {intent.putExtra(FACEBOOK_PROXY_AUTH_E2E_KEY, e2e);}intent.putExtra(ServerProtocol.DIALOG_PARAM_RESPONSE_TYPE, ServerProtocol.DIALOG_RESPONSE_TYPE_TOKEN);intent.putExtra(ServerProtocol.DIALOG_PARAM_RETURN_SCOPES, ServerProtocol.DIALOG_RETURN_SCOPES_TRUE);intent.putExtra(ServerProtocol.DIALOG_PARAM_DEFAULT_AUDIENCE, defaultAudience.getNativeProtocolAudience());if (!Settings.getPlatformCompatibilityEnabled()) {// Override the API Version for Authintent.putExtra(ServerProtocol.DIALOG_PARAM_LEGACY_OVERRIDE, ServerProtocol.GRAPH_API_VERSION);// Only set the rerequest auth type for non legacy requestsif (isRerequest) {intent.putExtra(ServerProtocol.DIALOG_PARAM_AUTH_TYPE, ServerProtocol.DIALOG_REREQUEST_AUTH_TYPE);}}intent = validateActivityIntent(context, intent, appInfo);if (intent != null) {return intent;}}return null;}public static Intent createTokenRefreshIntent(Context context) {for (NativeAppInfo appInfo : facebookAppInfoList) {Intent intent = new Intent().setClassName(appInfo.getPackage(), FACEBOOK_TOKEN_REFRESH_ACTIVITY);intent = validateServiceIntent(context, intent, appInfo);if (intent != null) {return intent;}}return null;}public static final int getLatestKnownVersion() {return KNOWN_PROTOCOL_VERSIONS.get(0);}// Note: be sure this stays sorted in descending order; add new versions at the beginningprivate static final List<Integer> KNOWN_PROTOCOL_VERSIONS =Arrays.asList(PROTOCOL_VERSION_20141107,PROTOCOL_VERSION_20141028,PROTOCOL_VERSION_20141001,PROTOCOL_VERSION_20140701,PROTOCOL_VERSION_20140324,PROTOCOL_VERSION_20140204,PROTOCOL_VERSION_20131107,PROTOCOL_VERSION_20130618,PROTOCOL_VERSION_20130502,PROTOCOL_VERSION_20121101);private static Intent findActivityIntent(Context context, String activityAction, String internalAction) {List<NativeAppInfo> list = actionToAppInfoMap.get(internalAction);if (list == null) {return null;}Intent intent = null;for (NativeAppInfo appInfo : list) {intent = new Intent().setAction(activityAction).setPackage(appInfo.getPackage()).addCategory(Intent.CATEGORY_DEFAULT);intent = validateActivityIntent(context, intent, appInfo);if (intent != null) {return intent;}}return intent;}public static boolean isVersionCompatibleWithBucketedIntent(int version) {return KNOWN_PROTOCOL_VERSIONS.contains(version) && version >= PROTOCOL_VERSION_20140701;}public static Intent createPlatformActivityIntent(Context context,String callId,String action,int version,String applicationName,Bundle extras) {Intent intent = findActivityIntent(context, INTENT_ACTION_PLATFORM_ACTIVITY, action);if (intent == null) {return null;}String applicationId = Utility.getMetadataApplicationId(context);intent.putExtra(EXTRA_PROTOCOL_VERSION, version).putExtra(EXTRA_PROTOCOL_ACTION, action).putExtra(EXTRA_APPLICATION_ID, applicationId);if (isVersionCompatibleWithBucketedIntent(version)) {// This is a bucketed intentBundle bridgeArguments = new Bundle();bridgeArguments.putString(BRIDGE_ARG_ACTION_ID_STRING, callId);bridgeArguments.putString(BRIDGE_ARG_APP_NAME_STRING, applicationName);intent.putExtra(EXTRA_PROTOCOL_BRIDGE_ARGS, bridgeArguments);Bundle methodArguments = (extras == null) ? new Bundle() : extras;intent.putExtra(EXTRA_PROTOCOL_METHOD_ARGS, methodArguments);} else {// This is the older flat intentintent.putExtra(EXTRA_PROTOCOL_CALL_ID, callId);intent.putExtra(EXTRA_APPLICATION_NAME, applicationName);intent.putExtras(extras);}return intent;}public static Intent createPlatformServiceIntent(Context context) {for (NativeAppInfo appInfo : facebookAppInfoList) {Intent intent = new Intent(INTENT_ACTION_PLATFORM_SERVICE).setPackage(appInfo.getPackage()).addCategory(Intent.CATEGORY_DEFAULT);intent = validateServiceIntent(context, intent, appInfo);if (intent != null) {return intent;}}return null;}public static int getProtocolVersionFromIntent(Intent intent) {return intent.getIntExtra(EXTRA_PROTOCOL_VERSION, 0);}public static UUID getCallIdFromIntent(Intent intent) {if (intent == null) {return null;}int version = getProtocolVersionFromIntent(intent);String callIdString = null;if (isVersionCompatibleWithBucketedIntent(version)) {Bundle bridgeArgs = intent.getBundleExtra(EXTRA_PROTOCOL_BRIDGE_ARGS);if (bridgeArgs != null) {callIdString = bridgeArgs.getString(BRIDGE_ARG_ACTION_ID_STRING);}} else {callIdString = intent.getStringExtra(EXTRA_PROTOCOL_CALL_ID);}UUID callId = null;if (callIdString != null) {try {callId = UUID.fromString(callIdString);} catch (IllegalArgumentException exception) {}}return callId;}public static Bundle getBridgeArgumentsFromIntent(Intent intent) {int version = getProtocolVersionFromIntent(intent);if (!isVersionCompatibleWithBucketedIntent(version)) {return null;}return intent.getBundleExtra(EXTRA_PROTOCOL_BRIDGE_ARGS);}public static Bundle getSuccessResultsFromIntent(Intent resultIntent) {int version = getProtocolVersionFromIntent(resultIntent);Bundle extras = resultIntent.getExtras();if (!isVersionCompatibleWithBucketedIntent(version) || extras == null) {return extras;}return extras.getBundle(EXTRA_PROTOCOL_METHOD_RESULTS);}public static boolean isErrorResult(Intent resultIntent) {Bundle bridgeArgs = getBridgeArgumentsFromIntent(resultIntent);if (bridgeArgs != null) {return bridgeArgs.containsKey(BRIDGE_ARG_ERROR_BUNDLE);} else {return resultIntent.hasExtra(STATUS_ERROR_TYPE);}}public static Bundle getErrorDataFromResultIntent(Intent resultIntent) {if (!isErrorResult(resultIntent)) {return null;}Bundle bridgeArgs = getBridgeArgumentsFromIntent(resultIntent);if (bridgeArgs != null) {return bridgeArgs.getBundle(BRIDGE_ARG_ERROR_BUNDLE);}return resultIntent.getExtras();}public static Exception getExceptionFromErrorData(Bundle errorData) {if (errorData == null) {return null;}String type = errorData.getString(BRIDGE_ARG_ERROR_TYPE);if (type == null) {type = errorData.getString(STATUS_ERROR_TYPE);}String description = errorData.getString(BRIDGE_ARG_ERROR_DESCRIPTION);if (description == null) {description = errorData.getString(STATUS_ERROR_DESCRIPTION);}if (type != null && type.equalsIgnoreCase(ERROR_USER_CANCELED)) {return new FacebookOperationCanceledException(description);}/* TODO parse error values and create appropriate exception class */return new FacebookException(description);}public static int getLatestAvailableProtocolVersionForService(Context context, final int minimumVersion) {// Services are currently always against the Facebook Appreturn getLatestAvailableProtocolVersionForAppInfoList(context, facebookAppInfoList, new int[] {minimumVersion});}public static int getLatestAvailableProtocolVersionForAction(Context context, String action, int[] versionSpec) {List<NativeAppInfo> appInfoList = actionToAppInfoMap.get(action);return getLatestAvailableProtocolVersionForAppInfoList(context, appInfoList, versionSpec);}private static int getLatestAvailableProtocolVersionForAppInfoList(Context context, List<NativeAppInfo> appInfoList,int[] versionSpec) {if (appInfoList == null) {return NO_PROTOCOL_AVAILABLE;}// Could potentially cache the NativeAppInfo to latestProtocolVersionfor (NativeAppInfo appInfo : appInfoList) {int protocolVersion = getLatestAvailableProtocolVersionForAppInfo(context, appInfo, versionSpec);if (protocolVersion != NO_PROTOCOL_AVAILABLE) {return protocolVersion;}}return NO_PROTOCOL_AVAILABLE;}private static int getLatestAvailableProtocolVersionForAppInfo(Context context,NativeAppInfo appInfo,int[] versionSpec) {TreeSet<Integer> fbAppVersions = getAllAvailableProtocolVersionsForAppInfo(context, appInfo);return computeLatestAvailableVersionFromVersionSpec(fbAppVersions, getLatestKnownVersion(), versionSpec);}private static TreeSet<Integer> getAllAvailableProtocolVersionsForAppInfo(Context context,NativeAppInfo appInfo) {TreeSet<Integer> allAvailableVersions = new TreeSet<Integer>();ContentResolver contentResolver = context.getContentResolver();String [] projection = new String[]{ PLATFORM_PROVIDER_VERSION_COLUMN };Uri uri = buildPlatformProviderVersionURI(appInfo);Cursor c = null;try {c = contentResolver.query(uri, projection, null, null, null);if (c != null) {while (c.moveToNext()) {int version = c.getInt(c.getColumnIndex(PLATFORM_PROVIDER_VERSION_COLUMN));allAvailableVersions.add(version);}}} finally {if (c != null) {c.close();}}return allAvailableVersions;}/*** This is public to allow for testing. Developers are discouraged from using this method, since it may change without* notice.*/public static int computeLatestAvailableVersionFromVersionSpec(TreeSet<Integer> allAvailableFacebookAppVersions,int latestSdkVersion,int[] versionSpec) {// Remember that these ranges are sorted in ascending order and can be unbounded. So we are starting// from the end of the version-spec array and working backwards, to try get the newest possible versionint versionSpecIndex = versionSpec.length - 1;Iterator<Integer> fbAppVersionsIterator = allAvailableFacebookAppVersions.descendingIterator();int latestFacebookAppVersion = -1;while (fbAppVersionsIterator.hasNext()) {int fbAppVersion = fbAppVersionsIterator.next();// We're holding on to the greatest fb-app version available.latestFacebookAppVersion = Math.max(latestFacebookAppVersion, fbAppVersion);// If there is a newer version in the versionSpec, throw it away, we don't have itwhile (versionSpecIndex >= 0 && versionSpec[versionSpecIndex] > fbAppVersion) {versionSpecIndex--;}if (versionSpecIndex < 0) {// There was no fb app version that fell into any range in the versionSpec - or - the// versionSpec was empty, which means that this action is not supported.return NO_PROTOCOL_AVAILABLE;}// If we are here, we know we are within a range specified in the versionSpec. We should see if it is// a disabled or enabled range.if (versionSpec[versionSpecIndex] == fbAppVersion) {// if the versionSpecIndex is even, it is enabled; if odd, disabledreturn (versionSpecIndex % 2 == 0 ?Math.min(latestFacebookAppVersion, latestSdkVersion) :NO_PROTOCOL_AVAILABLE);}}return NO_PROTOCOL_AVAILABLE;}private static Uri buildPlatformProviderVersionURI(NativeAppInfo appInfo) {return Uri.parse(CONTENT_SCHEME + appInfo.getPackage() + PLATFORM_PROVIDER_VERSIONS);}}