Subversion Repositories SmartDukaan

Rev

Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
14792 manas 1
/**
2
 * Copyright 2010-present Facebook
3
 *
4
 * Licensed under the Apache License, Version 2.0 (the "License");
5
 * you may not use this file except in compliance with the License.
6
 * You may obtain a copy of the License at
7
 *
8
 *    http://www.apache.org/licenses/LICENSE-2.0
9
 *
10
 * Unless required by applicable law or agreed to in writing, software
11
 * distributed under the License is distributed on an "AS IS" BASIS,
12
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
 * See the License for the specific language governing permissions and
14
 * limitations under the License.
15
 */
16
 
17
package com.facebook.android;
18
 
19
import android.Manifest;
20
import android.app.Activity;
21
import android.content.*;
22
import android.content.pm.PackageInfo;
23
import android.content.pm.PackageManager;
24
import android.content.pm.PackageManager.NameNotFoundException;
25
import android.content.pm.ResolveInfo;
26
import android.content.pm.Signature;
27
import android.net.Uri;
28
import android.os.*;
29
import com.facebook.*;
30
import com.facebook.Session.StatusCallback;
31
 
32
import java.io.FileNotFoundException;
33
import java.io.IOException;
34
import java.lang.ref.WeakReference;
35
import java.net.MalformedURLException;
36
import java.util.Arrays;
37
import java.util.Collections;
38
import java.util.List;
39
 
40
/**
41
 * THIS CLASS SHOULD BE CONSIDERED DEPRECATED.
42
 * <p/>
43
 * All public members of this class are intentionally deprecated.
44
 * New code should instead use
45
 * {@link Session} to manage session state,
46
 * {@link Request} to make API requests, and
47
 * {@link com.facebook.widget.WebDialog} to make dialog requests.
48
 * <p/>
49
 * Adding @Deprecated to this class causes warnings in other deprecated classes
50
 * that reference this one.  That is the only reason this entire class is not
51
 * deprecated.
52
 *
53
 * @devDocDeprecated
54
 */
55
public class Facebook {
56
 
57
    // Strings used in the authorization flow
58
    @Deprecated
59
    public static final String REDIRECT_URI = "fbconnect://success";
60
    @Deprecated
61
    public static final String CANCEL_URI = "fbconnect://cancel";
62
    @Deprecated
63
    public static final String TOKEN = "access_token";
64
    @Deprecated
65
    public static final String EXPIRES = "expires_in";
66
    @Deprecated
67
    public static final String SINGLE_SIGN_ON_DISABLED = "service_disabled";
68
 
69
    @Deprecated
70
    public static final Uri ATTRIBUTION_ID_CONTENT_URI =
71
        Uri.parse("content://com.facebook.katana.provider.AttributionIdProvider");
72
    @Deprecated
73
    public static final String ATTRIBUTION_ID_COLUMN_NAME = "aid";
74
 
75
    @Deprecated
76
    public static final int FORCE_DIALOG_AUTH = -1;
77
 
78
    private static final String LOGIN = "oauth";
79
 
80
    // Used as default activityCode by authorize(). See authorize() below.
81
    private static final int DEFAULT_AUTH_ACTIVITY_CODE = 32665;
82
 
83
    // Facebook server endpoints: may be modified in a subclass for testing
84
    @Deprecated
85
    protected static String DIALOG_BASE_URL = "https://m.facebook.com/dialog/";
86
    @Deprecated
87
    protected static String GRAPH_BASE_URL = "https://graph.facebook.com/";
88
    @Deprecated
89
    protected static String RESTSERVER_URL = "https://api.facebook.com/restserver.php";
90
 
91
    private final Object lock = new Object();
92
 
93
    private String accessToken = null;
94
    private long accessExpiresMillisecondsAfterEpoch = 0;
95
    private long lastAccessUpdateMillisecondsAfterEpoch = 0;
96
    private String mAppId;
97
 
98
    private Activity pendingAuthorizationActivity;
99
    private String[] pendingAuthorizationPermissions;
100
    private Session pendingOpeningSession;
101
 
102
    private volatile Session session; // must synchronize this.sync to write
103
    private boolean sessionInvalidated; // must synchronize this.sync to access
104
    private SetterTokenCachingStrategy tokenCache;
105
    private volatile Session userSetSession;
106
 
107
    // If the last time we extended the access token was more than 24 hours ago
108
    // we try to refresh the access token again.
109
    final private static long REFRESH_TOKEN_BARRIER = 24L * 60L * 60L * 1000L;
110
 
111
    /**
112
     * Constructor for Facebook object.
113
     * 
114
     * @param appId
115
     *            Your Facebook application ID. Found at
116
     *            www.facebook.com/developers/apps.php.
117
     */
118
    @Deprecated
119
    public Facebook(String appId) {
120
        if (appId == null) {
121
            throw new IllegalArgumentException("You must specify your application ID when instantiating "
122
                    + "a Facebook object. See README for details.");
123
        }
124
        mAppId = appId;
125
    }
126
 
127
    /**
128
     * Default authorize method. Grants only basic permissions.
129
     * <p/>
130
     * See authorize() below for @params.
131
     * <p/>
132
     * This method is deprecated.  See {@link Facebook} and {@link Session} for more info.
133
     */
134
    @Deprecated
135
    public void authorize(Activity activity, final DialogListener listener) {
136
        authorize(activity, new String[]{}, DEFAULT_AUTH_ACTIVITY_CODE, SessionLoginBehavior.SSO_WITH_FALLBACK,
137
                listener);
138
    }
139
 
140
    /**
141
     * Authorize method that grants custom permissions.
142
     * <p/>
143
     * See authorize() below for @params.
144
     * <p/>
145
     * This method is deprecated.  See {@link Facebook} and {@link Session} for more info.
146
     */
147
    @Deprecated
148
    public void authorize(Activity activity, String[] permissions, final DialogListener listener) {
149
        authorize(activity, permissions, DEFAULT_AUTH_ACTIVITY_CODE, SessionLoginBehavior.SSO_WITH_FALLBACK, listener);
150
    }
151
 
152
    /**
153
     * Full authorize method.
154
     * <p/>
155
     * Starts either an Activity or a dialog which prompts the user to log in to
156
     * Facebook and grant the requested permissions to the given application.
157
     * <p/>
158
     * This method will, when possible, use Facebook's single sign-on for
159
     * Android to obtain an access token. This involves proxying a call through
160
     * the Facebook for Android stand-alone application, which will handle the
161
     * authentication flow, and return an OAuth access token for making API
162
     * calls.
163
     * <p/>
164
     * Because this process will not be available for all users, if single
165
     * sign-on is not possible, this method will automatically fall back to the
166
     * OAuth 2.0 User-Agent flow. In this flow, the user credentials are handled
167
     * by Facebook in an embedded WebView, not by the client application. As
168
     * such, the dialog makes a network request and renders HTML content rather
169
     * than a native UI. The access token is retrieved from a redirect to a
170
     * special URL that the WebView handles.
171
     * <p/>
172
     * Note that User credentials could be handled natively using the OAuth 2.0
173
     * Username and Password Flow, but this is not supported by this SDK.
174
     * <p/>
175
     * See http://developers.facebook.com/docs/authentication/ and
176
     * http://wiki.oauth.net/OAuth-2 for more details.
177
     * <p/>
178
     * Note that this method is asynchronous and the callback will be invoked in
179
     * the original calling thread (not in a background thread).
180
     * <p/>
181
     * Also note that requests may be made to the API without calling authorize
182
     * first, in which case only public information is returned.
183
     * <p/>
184
     * IMPORTANT: Note that single sign-on authentication will not function
185
     * correctly if you do not include a call to the authorizeCallback() method
186
     * in your onActivityResult() function! Please see below for more
187
     * information. single sign-on may be disabled by passing FORCE_DIALOG_AUTH
188
     * as the activityCode parameter in your call to authorize().
189
     * <p/>
190
     * This method is deprecated.  See {@link Facebook} and {@link Session} for more info.
191
     *
192
     * @param activity
193
     *            The Android activity in which we want to display the
194
     *            authorization dialog.
195
     * @param permissions
196
     *            A list of permissions required for this application: e.g.
197
     *            "read_stream", "publish_stream", "offline_access", etc. see
198
     *            http://developers.facebook.com/docs/authentication/permissions
199
     *            This parameter should not be null -- if you do not require any
200
     *            permissions, then pass in an empty String array.
201
     * @param activityCode
202
     *            Single sign-on requires an activity result to be called back
203
     *            to the client application -- if you are waiting on other
204
     *            activities to return data, pass a custom activity code here to
205
     *            avoid collisions. If you would like to force the use of legacy
206
     *            dialog-based authorization, pass FORCE_DIALOG_AUTH for this
207
     *            parameter. Otherwise just omit this parameter and Facebook
208
     *            will use a suitable default. See
209
     *            http://developer.android.com/reference/android/
210
     *            app/Activity.html for more information.
211
     * @param listener
212
     *            Callback interface for notifying the calling application when
213
     *            the authentication dialog has completed, failed, or been
214
     *            canceled.
215
     */
216
    @Deprecated
217
    public void authorize(Activity activity, String[] permissions, int activityCode, final DialogListener listener) {
218
        SessionLoginBehavior behavior = (activityCode >= 0) ? SessionLoginBehavior.SSO_WITH_FALLBACK
219
                : SessionLoginBehavior.SUPPRESS_SSO;
220
 
221
        authorize(activity, permissions, activityCode, behavior, listener);
222
    }
223
 
224
    /**
225
     * Full authorize method.
226
     * 
227
     * Starts either an Activity or a dialog which prompts the user to log in to
228
     * Facebook and grant the requested permissions to the given application.
229
     * 
230
     * This method will, when possible, use Facebook's single sign-on for
231
     * Android to obtain an access token. This involves proxying a call through
232
     * the Facebook for Android stand-alone application, which will handle the
233
     * authentication flow, and return an OAuth access token for making API
234
     * calls.
235
     * 
236
     * Because this process will not be available for all users, if single
237
     * sign-on is not possible, this method will automatically fall back to the
238
     * OAuth 2.0 User-Agent flow. In this flow, the user credentials are handled
239
     * by Facebook in an embedded WebView, not by the client application. As
240
     * such, the dialog makes a network request and renders HTML content rather
241
     * than a native UI. The access token is retrieved from a redirect to a
242
     * special URL that the WebView handles.
243
     * 
244
     * Note that User credentials could be handled natively using the OAuth 2.0
245
     * Username and Password Flow, but this is not supported by this SDK.
246
     * 
247
     * See http://developers.facebook.com/docs/authentication/ and
248
     * http://wiki.oauth.net/OAuth-2 for more details.
249
     * 
250
     * Note that this method is asynchronous and the callback will be invoked in
251
     * the original calling thread (not in a background thread).
252
     * 
253
     * Also note that requests may be made to the API without calling authorize
254
     * first, in which case only public information is returned.
255
     * 
256
     * IMPORTANT: Note that single sign-on authentication will not function
257
     * correctly if you do not include a call to the authorizeCallback() method
258
     * in your onActivityResult() function! Please see below for more
259
     * information. single sign-on may be disabled by passing FORCE_DIALOG_AUTH
260
     * as the activityCode parameter in your call to authorize().
261
     * 
262
     * @param activity
263
     *            The Android activity in which we want to display the
264
     *            authorization dialog.
265
     * @param permissions
266
     *            A list of permissions required for this application: e.g.
267
     *            "read_stream", "publish_stream", "offline_access", etc. see
268
     *            http://developers.facebook.com/docs/authentication/permissions
269
     *            This parameter should not be null -- if you do not require any
270
     *            permissions, then pass in an empty String array.
271
     * @param activityCode
272
     *            Single sign-on requires an activity result to be called back
273
     *            to the client application -- if you are waiting on other
274
     *            activities to return data, pass a custom activity code here to
275
     *            avoid collisions. If you would like to force the use of legacy
276
     *            dialog-based authorization, pass FORCE_DIALOG_AUTH for this
277
     *            parameter. Otherwise just omit this parameter and Facebook
278
     *            will use a suitable default. See
279
     *            http://developer.android.com/reference/android/
280
     *            app/Activity.html for more information.
281
     * @param behavior
282
     *            The {@link SessionLoginBehavior SessionLoginBehavior} that
283
     *            specifies what behaviors should be attempted during
284
     *            authorization.
285
     * @param listener
286
     *            Callback interface for notifying the calling application when
287
     *            the authentication dialog has completed, failed, or been
288
     *            canceled.
289
     */
290
    private void authorize(Activity activity, String[] permissions, int activityCode,
291
                          SessionLoginBehavior behavior, final DialogListener listener) {
292
        checkUserSession("authorize");
293
        pendingOpeningSession = new Session.Builder(activity).
294
                setApplicationId(mAppId).
295
                setTokenCachingStrategy(getTokenCache()).
296
                build();
297
        pendingAuthorizationActivity = activity;
298
        pendingAuthorizationPermissions = (permissions != null) ? permissions : new String[0];
299
 
300
        StatusCallback callback = new StatusCallback() {
301
            @Override
302
            public void call(Session callbackSession, SessionState state, Exception exception) {
303
                // Invoke user-callback.
304
                onSessionCallback(callbackSession, state, exception, listener);
305
            }
306
        };
307
 
308
        Session.OpenRequest openRequest = new Session.OpenRequest(activity).
309
                setCallback(callback).
310
                setLoginBehavior(behavior).
311
                setRequestCode(activityCode).
312
                setPermissions(Arrays.asList(pendingAuthorizationPermissions));
313
        openSession(pendingOpeningSession, openRequest, pendingAuthorizationPermissions.length > 0);
314
    }
315
 
316
    private void openSession(Session session, Session.OpenRequest openRequest, boolean isPublish) {
317
        openRequest.setIsLegacy(true);
318
        if (isPublish) {
319
            session.openForPublish(openRequest);
320
        } else {
321
            session.openForRead(openRequest);
322
        }
323
    }
324
 
325
    @SuppressWarnings("deprecation")
326
    private void onSessionCallback(Session callbackSession, SessionState state, Exception exception,
327
            DialogListener listener) {
328
        Bundle extras = callbackSession.getAuthorizationBundle();
329
 
330
        if (state == SessionState.OPENED) {
331
            Session sessionToClose = null;
332
 
333
            synchronized (Facebook.this.lock) {
334
                if (callbackSession != Facebook.this.session) {
335
                    sessionToClose = Facebook.this.session;
336
                    Facebook.this.session = callbackSession;
337
                    Facebook.this.sessionInvalidated = false;
338
                }
339
            }
340
 
341
            if (sessionToClose != null) {
342
                sessionToClose.close();
343
            }
344
 
345
            listener.onComplete(extras);
346
        } else if (exception != null) {
347
            if (exception instanceof FacebookOperationCanceledException) {
348
                listener.onCancel();
349
            } else if ((exception instanceof FacebookAuthorizationException) && (extras != null)
350
                    && extras.containsKey(Session.WEB_VIEW_ERROR_CODE_KEY)
351
                    && extras.containsKey(Session.WEB_VIEW_FAILING_URL_KEY)) {
352
                DialogError error = new DialogError(exception.getMessage(),
353
                        extras.getInt(Session.WEB_VIEW_ERROR_CODE_KEY),
354
                        extras.getString(Session.WEB_VIEW_FAILING_URL_KEY));
355
                listener.onError(error);
356
            } else {
357
                FacebookError error = new FacebookError(exception.getMessage());
358
                listener.onFacebookError(error);
359
            }
360
        }
361
    }
362
 
363
    /**
364
     * Helper to validate a service intent by resolving and checking the
365
     * provider's package signature.
366
     * 
367
     * @param context
368
     * @param intent
369
     * @return true if the service intent resolution happens successfully and
370
     *         the signatures match.
371
     */
372
    private boolean validateServiceIntent(Context context, Intent intent) {
373
        ResolveInfo resolveInfo = context.getPackageManager().resolveService(intent, 0);
374
        if (resolveInfo == null) {
375
            return false;
376
        }
377
 
378
        return validateAppSignatureForPackage(context, resolveInfo.serviceInfo.packageName);
379
    }
380
 
381
    /**
382
     * Query the signature for the application that would be invoked by the
383
     * given intent and verify that it matches the FB application's signature.
384
     * 
385
     * @param context
386
     * @param packageName
387
     * @return true if the app's signature matches the expected signature.
388
     */
389
    private boolean validateAppSignatureForPackage(Context context, String packageName) {
390
 
391
        PackageInfo packageInfo;
392
        try {
393
            packageInfo = context.getPackageManager().getPackageInfo(packageName, PackageManager.GET_SIGNATURES);
394
        } catch (NameNotFoundException e) {
395
            return false;
396
        }
397
 
398
        for (Signature signature : packageInfo.signatures) {
399
            if (signature.toCharsString().equals(FB_APP_SIGNATURE)) {
400
                return true;
401
            }
402
        }
403
        return false;
404
    }
405
 
406
    /**
407
     * IMPORTANT: If you are using the deprecated authorize() method,
408
     * this method must be invoked at the top of the calling
409
     * activity's onActivityResult() function or Facebook authentication will
410
     * not function properly!
411
     * <p/>
412
     * If your calling activity does not currently implement onActivityResult(),
413
     * you must implement it and include a call to this method if you intend to
414
     * use the authorize() method in this SDK.
415
     * <p/>
416
     * For more information, see
417
     * http://developer.android.com/reference/android/app/
418
     * Activity.html#onActivityResult(int, int, android.content.Intent)
419
     * <p/>
420
     * This method is deprecated.  See {@link Facebook} and {@link Session} for more info.
421
     */
422
    @Deprecated
423
    public void authorizeCallback(int requestCode, int resultCode, Intent data) {
424
        checkUserSession("authorizeCallback");
425
        Session pending = this.pendingOpeningSession;
426
        if (pending != null) {
427
            if (pending.onActivityResult(this.pendingAuthorizationActivity, requestCode, resultCode, data)) {
428
                this.pendingOpeningSession = null;
429
                this.pendingAuthorizationActivity = null;
430
                this.pendingAuthorizationPermissions = null;
431
            }
432
        }
433
    }
434
 
435
    /**
436
     * Refresh OAuth access token method. Binds to Facebook for Android
437
     * stand-alone application application to refresh the access token. This
438
     * method tries to connect to the Facebook App which will handle the
439
     * authentication flow, and return a new OAuth access token. This method
440
     * will automatically replace the old token with a new one. Note that this
441
     * method is asynchronous and the callback will be invoked in the original
442
     * calling thread (not in a background thread).
443
     * <p/>
444
     * This method is deprecated.  See {@link Facebook} and {@link Session} for more info.
445
     *
446
     * @param context
447
     *            The Android Context that will be used to bind to the Facebook
448
     *            RefreshToken Service
449
     * @param serviceListener
450
     *            Callback interface for notifying the calling application when
451
     *            the refresh request has completed or failed (can be null). In
452
     *            case of a success a new token can be found inside the result
453
     *            Bundle under Facebook.ACCESS_TOKEN key.
454
     * @return true if the binding to the RefreshToken Service was created
455
     */
456
    @Deprecated
457
    public boolean extendAccessToken(Context context, ServiceListener serviceListener) {
458
        checkUserSession("extendAccessToken");
459
        Intent intent = new Intent();
460
 
461
        intent.setClassName("com.facebook.katana", "com.facebook.katana.platform.TokenRefreshService");
462
 
463
        // Verify that the application whose package name is
464
        // com.facebook.katana
465
        // has the expected FB app signature.
466
        if (!validateServiceIntent(context, intent)) {
467
            return false;
468
        }
469
 
470
        return context.bindService(intent, new TokenRefreshServiceConnection(context, serviceListener),
471
                Context.BIND_AUTO_CREATE);
472
    }
473
 
474
    /**
475
     * Calls extendAccessToken if shouldExtendAccessToken returns true.
476
     * <p/>
477
     * This method is deprecated.  See {@link Facebook} and {@link Session} for more info.
478
     *
479
     * @return the same value as extendAccessToken if the the token requires
480
     *         refreshing, true otherwise
481
     */
482
    @Deprecated
483
    public boolean extendAccessTokenIfNeeded(Context context, ServiceListener serviceListener) {
484
        checkUserSession("extendAccessTokenIfNeeded");
485
        if (shouldExtendAccessToken()) {
486
            return extendAccessToken(context, serviceListener);
487
        }
488
        return true;
489
    }
490
 
491
    /**
492
     * Check if the access token requires refreshing.
493
     * <p/>
494
     * This method is deprecated.  See {@link Facebook} and {@link Session} for more info.
495
     *
496
     * @return true if the last time a new token was obtained was over 24 hours
497
     *         ago.
498
     */
499
    @Deprecated
500
    public boolean shouldExtendAccessToken() {
501
        checkUserSession("shouldExtendAccessToken");
502
        return isSessionValid()
503
                && (System.currentTimeMillis() - lastAccessUpdateMillisecondsAfterEpoch >= REFRESH_TOKEN_BARRIER);
504
    }
505
 
506
    /**
507
     * Handles connection to the token refresh service (this service is a part
508
     * of Facebook App).
509
     */
510
    private class TokenRefreshServiceConnection implements ServiceConnection {
511
 
512
        final Messenger messageReceiver = new Messenger(
513
                new TokenRefreshConnectionHandler(Facebook.this, this));
514
 
515
        final ServiceListener serviceListener;
516
        final Context applicationsContext;
517
 
518
        Messenger messageSender = null;
519
 
520
        public TokenRefreshServiceConnection(Context applicationsContext, ServiceListener serviceListener) {
521
            this.applicationsContext = applicationsContext;
522
            this.serviceListener = serviceListener;
523
        }
524
 
525
        @Override
526
        public void onServiceConnected(ComponentName className, IBinder service) {
527
            messageSender = new Messenger(service);
528
            refreshToken();
529
        }
530
 
531
        @Override
532
        public void onServiceDisconnected(ComponentName arg) {
533
            serviceListener.onError(new Error("Service disconnected"));
534
            try {
535
                // We returned an error so there's no point in
536
                // keeping the binding open.
537
                applicationsContext.unbindService(TokenRefreshServiceConnection.this);
538
            } catch (IllegalArgumentException ex) {
539
                // Do nothing, the connection was already unbound
540
            }
541
        }
542
 
543
        private void refreshToken() {
544
            Bundle requestData = new Bundle();
545
            requestData.putString(TOKEN, accessToken);
546
 
547
            Message request = Message.obtain();
548
            request.setData(requestData);
549
            request.replyTo = messageReceiver;
550
 
551
            try {
552
                messageSender.send(request);
553
            } catch (RemoteException e) {
554
                serviceListener.onError(new Error("Service connection error"));
555
            }
556
        }
557
    }
558
 
559
    // Creating a static Handler class to reduce the possibility of a memory leak.
560
    // Handler objects for the same thread all share a common Looper object, which they post messages
561
    // to and read from. As messages contain target Handler, as long as there are messages with target
562
    // handler in the message queue, the handler cannot be garbage collected. If handler is not static,
563
    // the instance of the containing class also cannot be garbage collected even if it is destroyed.
564
    private static class TokenRefreshConnectionHandler extends Handler {
565
        WeakReference<Facebook> facebookWeakReference;
566
        WeakReference<TokenRefreshServiceConnection> connectionWeakReference;
567
 
568
        TokenRefreshConnectionHandler(Facebook facebook, TokenRefreshServiceConnection connection) {
569
            super();
570
            facebookWeakReference = new WeakReference<Facebook>(facebook);
571
            connectionWeakReference = new WeakReference<TokenRefreshServiceConnection>(connection);
572
        }
573
 
574
        @Override
575
        @SuppressWarnings("deprecation")
576
        public void handleMessage(Message msg) {
577
            Facebook facebook = facebookWeakReference.get();
578
            TokenRefreshServiceConnection connection = connectionWeakReference.get();
579
            if (facebook == null || connection == null) {
580
                return;
581
            }
582
 
583
            String token = msg.getData().getString(TOKEN);
584
            // Legacy functions in Facebook class (and ServiceListener implementors) expect expires_in in
585
            // milliseconds from epoch
586
            long expiresAtMsecFromEpoch = msg.getData().getLong(EXPIRES) * 1000L;
587
 
588
            if (token != null) {
589
                facebook.setAccessToken(token);
590
                facebook.setAccessExpires(expiresAtMsecFromEpoch);
591
 
592
                Session refreshSession = facebook.session;
593
                if (refreshSession != null) {
594
                    // Session.internalRefreshToken expects the original bundle with expires_in in seconds from
595
                    // epoch.
596
                    LegacyHelper.extendTokenCompleted(refreshSession, msg.getData());
597
                }
598
 
599
                if (connection.serviceListener != null) {
600
                    // To avoid confusion we should return the expiration time in
601
                    // the same format as the getAccessExpires() function - that
602
                    // is in milliseconds.
603
                    Bundle resultBundle = (Bundle) msg.getData().clone();
604
                    resultBundle.putLong(EXPIRES, expiresAtMsecFromEpoch);
605
 
606
                    connection.serviceListener.onComplete(resultBundle);
607
                }
608
            } else if (connection.serviceListener != null) { // extract errors only if
609
                // client wants them
610
                String error = msg.getData().getString("error");
611
                if (msg.getData().containsKey("error_code")) {
612
                    int errorCode = msg.getData().getInt("error_code");
613
                    connection.serviceListener.onFacebookError(new FacebookError(error, null, errorCode));
614
                } else {
615
                    connection.serviceListener.onError(new Error(error != null ? error : "Unknown service error"));
616
                }
617
            }
618
 
619
            // The refreshToken function should be called rarely,
620
            // so there is no point in keeping the binding open.
621
            connection.applicationsContext.unbindService(connection);
622
        }
623
    }
624
 
625
    /**
626
     * Invalidate the current user session by removing the access token in
627
     * memory, clearing the browser cookie, and calling auth.expireSession
628
     * through the API.
629
     * <p/>
630
     * Note that this method blocks waiting for a network response, so do not
631
     * call it in a UI thread.
632
     * <p/>
633
     * This method is deprecated.  See {@link Facebook} and {@link Session} for more info.
634
     *
635
     * @param context
636
     *            The Android context in which the logout should be called: it
637
     *            should be the same context in which the login occurred in
638
     *            order to clear any stored cookies
639
     * @throws IOException
640
     * @throws MalformedURLException
641
     * @return JSON string representation of the auth.expireSession response
642
     *         ("true" if successful)
643
     */
644
    @Deprecated
645
    public String logout(Context context) throws MalformedURLException, IOException {
646
        return logoutImpl(context);
647
    }
648
 
649
    String logoutImpl(Context context) throws MalformedURLException, IOException  {
650
        checkUserSession("logout");
651
        Bundle b = new Bundle();
652
        b.putString("method", "auth.expireSession");
653
        String response = request(b);
654
 
655
        long currentTimeMillis = System.currentTimeMillis();
656
        Session sessionToClose = null;
657
 
658
        synchronized (this.lock) {
659
            sessionToClose = session;
660
 
661
            session = null;
662
            accessToken = null;
663
            accessExpiresMillisecondsAfterEpoch = 0;
664
            lastAccessUpdateMillisecondsAfterEpoch = currentTimeMillis;
665
            sessionInvalidated = false;
666
        }
667
 
668
        if (sessionToClose != null) {
669
            sessionToClose.closeAndClearTokenInformation();
670
        }
671
 
672
        return response;
673
    }
674
 
675
    /**
676
     * Make a request to Facebook's old (pre-graph) API with the given
677
     * parameters. One of the parameter keys must be "method" and its value
678
     * should be a valid REST server API method.
679
     * <p/>
680
     * See http://developers.facebook.com/docs/reference/rest/
681
     * <p/>
682
     * Note that this method blocks waiting for a network response, so do not
683
     * call it in a UI thread.
684
     * <p/>
685
     * Example: <code>
686
     *  Bundle parameters = new Bundle();
687
     *  parameters.putString("method", "auth.expireSession");
688
     *  String response = request(parameters);
689
     * </code>
690
     * <p/>
691
     * This method is deprecated.  See {@link Facebook} and {@link Request} for more info.
692
     *
693
     * @param parameters
694
     *            Key-value pairs of parameters to the request. Refer to the
695
     *            documentation: one of the parameters must be "method".
696
     * @throws IOException
697
     *             if a network error occurs
698
     * @throws MalformedURLException
699
     *             if accessing an invalid endpoint
700
     * @throws IllegalArgumentException
701
     *             if one of the parameters is not "method"
702
     * @return JSON string representation of the response
703
     */
704
    @Deprecated
705
    public String request(Bundle parameters) throws MalformedURLException, IOException {
706
        if (!parameters.containsKey("method")) {
707
            throw new IllegalArgumentException("API method must be specified. "
708
                    + "(parameters must contain key \"method\" and value). See"
709
                    + " http://developers.facebook.com/docs/reference/rest/");
710
        }
711
        return requestImpl(null, parameters, "GET");
712
    }
713
 
714
    /**
715
     * Make a request to the Facebook Graph API without any parameters.
716
     * <p/>
717
     * See http://developers.facebook.com/docs/api
718
     * <p/>
719
     * Note that this method blocks waiting for a network response, so do not
720
     * call it in a UI thread.
721
     * <p/>
722
     * This method is deprecated.  See {@link Facebook} and {@link Request} for more info.
723
     *
724
     * @param graphPath
725
     *            Path to resource in the Facebook graph, e.g., to fetch data
726
     *            about the currently logged authenticated user, provide "me",
727
     *            which will fetch http://graph.facebook.com/me
728
     * @throws IOException
729
     * @throws MalformedURLException
730
     * @return JSON string representation of the response
731
     */
732
    @Deprecated
733
    public String request(String graphPath) throws MalformedURLException, IOException {
734
        return requestImpl(graphPath, new Bundle(), "GET");
735
    }
736
 
737
    /**
738
     * Make a request to the Facebook Graph API with the given string parameters
739
     * using an HTTP GET (default method).
740
     * <p/>
741
     * See http://developers.facebook.com/docs/api
742
     * <p/>
743
     * Note that this method blocks waiting for a network response, so do not
744
     * call it in a UI thread.
745
     * <p/>
746
     * This method is deprecated.  See {@link Facebook} and {@link Request} for more info.
747
     *
748
     * @param graphPath
749
     *            Path to resource in the Facebook graph, e.g., to fetch data
750
     *            about the currently logged authenticated user, provide "me",
751
     *            which will fetch http://graph.facebook.com/me
752
     * @param parameters
753
     *            key-value string parameters, e.g. the path "search" with
754
     *            parameters "q" : "facebook" would produce a query for the
755
     *            following graph resource:
756
     *            https://graph.facebook.com/search?q=facebook
757
     * @throws IOException
758
     * @throws MalformedURLException
759
     * @return JSON string representation of the response
760
     */
761
    @Deprecated
762
    public String request(String graphPath, Bundle parameters) throws MalformedURLException, IOException {
763
        return requestImpl(graphPath, parameters, "GET");
764
    }
765
 
766
    /**
767
     * Synchronously make a request to the Facebook Graph API with the given
768
     * HTTP method and string parameters. Note that binary data parameters (e.g.
769
     * pictures) are not yet supported by this helper function.
770
     * <p/>
771
     * See http://developers.facebook.com/docs/api
772
     * <p/>
773
     * Note that this method blocks waiting for a network response, so do not
774
     * call it in a UI thread.
775
     * <p/>
776
     * This method is deprecated.  See {@link Facebook} and {@link Request} for more info.
777
     *
778
     * @param graphPath
779
     *            Path to resource in the Facebook graph, e.g., to fetch data
780
     *            about the currently logged authenticated user, provide "me",
781
     *            which will fetch http://graph.facebook.com/me
782
     * @param params
783
     *            Key-value string parameters, e.g. the path "search" with
784
     *            parameters {"q" : "facebook"} would produce a query for the
785
     *            following graph resource:
786
     *            https://graph.facebook.com/search?q=facebook
787
     * @param httpMethod
788
     *            http verb, e.g. "GET", "POST", "DELETE"
789
     * @throws IOException
790
     * @throws MalformedURLException
791
     * @return JSON string representation of the response
792
     */
793
    @Deprecated
794
    public String request(String graphPath, Bundle params, String httpMethod) throws FileNotFoundException,
795
            MalformedURLException, IOException {
796
        return requestImpl(graphPath, params, httpMethod);
797
    }
798
 
799
    // Internal call to avoid deprecated warnings.
800
    @SuppressWarnings("deprecation")
801
    String requestImpl(String graphPath, Bundle params, String httpMethod) throws FileNotFoundException,
802
            MalformedURLException, IOException {
803
        params.putString("format", "json");
804
        if (isSessionValid()) {
805
            params.putString(TOKEN, getAccessToken());
806
        }
807
        String url = (graphPath != null) ? GRAPH_BASE_URL + graphPath : RESTSERVER_URL;
808
        return Util.openUrl(url, httpMethod, params);
809
    }
810
 
811
    /**
812
     * Generate a UI dialog for the request action in the given Android context.
813
     * <p/>
814
     * Note that this method is asynchronous and the callback will be invoked in
815
     * the original calling thread (not in a background thread).
816
     *
817
     * This method is deprecated. See {@link com.facebook.widget.WebDialog}.
818
     *
819
     * @param context
820
     *            The Android context in which we will generate this dialog.
821
     * @param action
822
     *            String representation of the desired method: e.g. "login",
823
     *            "stream.publish", ...
824
     * @param listener
825
     *            Callback interface to notify the application when the dialog
826
     *            has completed.
827
     */
828
    @Deprecated
829
    public void dialog(Context context, String action, DialogListener listener) {
830
        dialog(context, action, new Bundle(), listener);
831
    }
832
 
833
    /**
834
     * Generate a UI dialog for the request action in the given Android context
835
     * with the provided parameters.
836
     * <p/>
837
     * Note that this method is asynchronous and the callback will be invoked in
838
     * the original calling thread (not in a background thread).
839
     *
840
     * This method is deprecated. See {@link com.facebook.widget.WebDialog}.
841
     * 
842
     * @param context
843
     *            The Android context in which we will generate this dialog.
844
     * @param action
845
     *            String representation of the desired method: e.g. "feed" ...
846
     * @param parameters
847
     *            String key-value pairs to be passed as URL parameters.
848
     * @param listener
849
     *            Callback interface to notify the application when the dialog
850
     *            has completed.
851
     */
852
    @Deprecated
853
    public void dialog(Context context, String action, Bundle parameters, final DialogListener listener) {
854
        parameters.putString("display", "touch");
855
        parameters.putString("redirect_uri", REDIRECT_URI);
856
 
857
        if (action.equals(LOGIN)) {
858
            parameters.putString("type", "user_agent");
859
            parameters.putString("client_id", mAppId);
860
        } else {
861
            parameters.putString("app_id", mAppId);
862
            // We do not want to add an access token when displaying the auth dialog.
863
            if (isSessionValid()) {
864
                parameters.putString(TOKEN, getAccessToken());
865
            }
866
        }
867
 
868
        if (context.checkCallingOrSelfPermission(Manifest.permission.INTERNET) != PackageManager.PERMISSION_GRANTED) {
869
            Util.showAlert(context, "Error", "Application requires permission to access the Internet");
870
        } else {
871
            new FbDialog(context, action, parameters, listener).show();
872
        }
873
    }
874
 
875
    /**
876
     * Returns whether the current access token is valid
877
     *
878
     * @return boolean - whether this object has an non-expired session token
879
     */
880
    @Deprecated
881
    public boolean isSessionValid() {
882
        return (getAccessToken() != null)
883
                && ((getAccessExpires() == 0) || (System.currentTimeMillis() < getAccessExpires()));
884
    }
885
 
886
    /**
887
     * Allows the user to set a Session for the Facebook class to use.
888
     * If a Session is set here, then one should not use the authorize, logout,
889
     * or extendAccessToken methods which alter the Session object since that may
890
     * result in undefined behavior. Using those methods after setting the
891
     * session here will result in exceptions being thrown.
892
     *
893
     * @param session the Session object to use, cannot be null
894
     */
895
    @Deprecated
896
    public void setSession(Session session) {
897
        if (session == null) {
898
            throw new IllegalArgumentException("session cannot be null");
899
        }
900
        synchronized (this.lock) {
901
            this.userSetSession = session;
902
        }
903
    }
904
 
905
    private void checkUserSession(String methodName) {
906
        if (userSetSession != null) {
907
            throw new UnsupportedOperationException(
908
                    String.format("Cannot call %s after setSession has been called.", methodName));
909
        }
910
    }
911
 
912
    /**
913
     * Get the underlying Session object to use with 3.0 api.
914
     * 
915
     * @return Session - underlying session
916
     */
917
    @Deprecated
918
    public final Session getSession() {
919
        while (true) {
920
            String cachedToken = null;
921
            Session oldSession = null;
922
 
923
            synchronized (this.lock) {
924
                if (userSetSession != null) {
925
                    return userSetSession;
926
                }
927
                if ((session != null) || !sessionInvalidated) {
928
                    return session;
929
                }
930
 
931
                cachedToken = accessToken;
932
                oldSession = session;
933
            }
934
 
935
            if (cachedToken == null) {
936
                return null;
937
            }
938
 
939
            // At this point we do not have a valid session, but mAccessToken is
940
            // non-null.
941
            // So we can try building a session based on that.
942
            List<String> permissions;
943
            if (oldSession != null) {
944
                permissions = oldSession.getPermissions();
945
            } else if (pendingAuthorizationPermissions != null) {
946
                permissions = Arrays.asList(pendingAuthorizationPermissions);
947
            } else {
948
                permissions = Collections.<String>emptyList();
949
            }
950
 
951
            Session newSession = new Session.Builder(pendingAuthorizationActivity).
952
                    setApplicationId(mAppId).
953
                    setTokenCachingStrategy(getTokenCache()).
954
                    build();
955
            if (newSession.getState() != SessionState.CREATED_TOKEN_LOADED) {
956
                return null;
957
            }
958
            Session.OpenRequest openRequest =
959
                    new Session.OpenRequest(pendingAuthorizationActivity).setPermissions(permissions);
960
            openSession(newSession, openRequest, !permissions.isEmpty());
961
 
962
            Session invalidatedSession = null;
963
            Session returnSession = null;
964
 
965
            synchronized (this.lock) {
966
                if (sessionInvalidated || (session == null)) {
967
                    invalidatedSession = session;
968
                    returnSession = session = newSession;
969
                    sessionInvalidated = false;
970
                }
971
            }
972
 
973
            if (invalidatedSession != null) {
974
                invalidatedSession.close();
975
            }
976
 
977
            if (returnSession != null) {
978
                return returnSession;
979
            }
980
            // Else token state changed between the synchronized blocks, so
981
            // retry..
982
        }
983
    }
984
 
985
    /**
986
     * Retrieve the OAuth 2.0 access token for API access: treat with care.
987
     * Returns null if no session exists.
988
     *
989
     * @return String - access token
990
     */
991
    @Deprecated
992
    public String getAccessToken() {
993
        Session s = getSession();
994
        if (s != null) {
995
            return s.getAccessToken();
996
        } else {
997
            return null;
998
        }
999
    }
1000
 
1001
    /**
1002
     * Retrieve the current session's expiration time (in milliseconds since
1003
     * Unix epoch), or 0 if the session doesn't expire or doesn't exist.
1004
     *
1005
     * @return long - session expiration time
1006
     */
1007
    @Deprecated
1008
    public long getAccessExpires() {
1009
        Session s = getSession();
1010
        if (s != null) {
1011
            return s.getExpirationDate().getTime();
1012
        } else {
1013
            return accessExpiresMillisecondsAfterEpoch;
1014
        }
1015
    }
1016
 
1017
    /**
1018
     * Retrieve the last time the token was updated (in milliseconds since
1019
     * the Unix epoch), or 0 if the token has not been set.
1020
     *
1021
     * @return long - timestamp of the last token update.
1022
     */
1023
    @Deprecated
1024
    public long getLastAccessUpdate() {
1025
        return lastAccessUpdateMillisecondsAfterEpoch;
1026
    }
1027
 
1028
    /**
1029
     * Restore the token, expiration time, and last update time from cached values.
1030
     * These should be values obtained from getAccessToken(), getAccessExpires, and
1031
     * getLastAccessUpdate() respectively.
1032
     * <p/>
1033
     * This method is deprecated.  See {@link Facebook} and {@link Session} for more info.
1034
     *
1035
     * @param accessToken - access token
1036
     * @param accessExpires - access token expiration time
1037
     * @param lastAccessUpdate - timestamp of the last token update
1038
     */
1039
    @Deprecated
1040
    public void setTokenFromCache(String accessToken, long accessExpires, long lastAccessUpdate) {
1041
        checkUserSession("setTokenFromCache");
1042
        synchronized (this.lock) {
1043
            this.accessToken = accessToken;
1044
            accessExpiresMillisecondsAfterEpoch = accessExpires;
1045
            lastAccessUpdateMillisecondsAfterEpoch = lastAccessUpdate;
1046
        }
1047
    }
1048
 
1049
    /**
1050
     * Set the OAuth 2.0 access token for API access.
1051
     * <p/>
1052
     * This method is deprecated.  See {@link Facebook} and {@link Session} for more info.
1053
     *
1054
     * @param token
1055
     *            - access token
1056
     */
1057
    @Deprecated
1058
    public void setAccessToken(String token) {
1059
        checkUserSession("setAccessToken");
1060
        synchronized (this.lock) {
1061
            accessToken = token;
1062
            lastAccessUpdateMillisecondsAfterEpoch = System.currentTimeMillis();
1063
            sessionInvalidated = true;
1064
        }
1065
    }
1066
 
1067
    /**
1068
     * Set the current session's expiration time (in milliseconds since Unix
1069
     * epoch), or 0 if the session doesn't expire.
1070
     * <p/>
1071
     * This method is deprecated.  See {@link Facebook} and {@link Session} for more info.
1072
     *
1073
     * @param timestampInMsec
1074
     *            - timestamp in milliseconds
1075
     */
1076
    @Deprecated
1077
    public void setAccessExpires(long timestampInMsec) {
1078
        checkUserSession("setAccessExpires");
1079
        synchronized (this.lock) {
1080
            accessExpiresMillisecondsAfterEpoch = timestampInMsec;
1081
            lastAccessUpdateMillisecondsAfterEpoch = System.currentTimeMillis();
1082
            sessionInvalidated = true;
1083
        }
1084
    }
1085
 
1086
    /**
1087
     * Set the current session's duration (in seconds since Unix epoch), or "0"
1088
     * if session doesn't expire.
1089
     * <p/>
1090
     * This method is deprecated.  See {@link Facebook} and {@link Session} for more info.
1091
     *
1092
     * @param expiresInSecsFromNow
1093
     *            - duration in seconds (or 0 if the session doesn't expire)
1094
     */
1095
    @Deprecated
1096
    public void setAccessExpiresIn(String expiresInSecsFromNow) {
1097
        checkUserSession("setAccessExpiresIn");
1098
        if (expiresInSecsFromNow != null) {
1099
            long expires = expiresInSecsFromNow.equals("0") ? 0 : System.currentTimeMillis()
1100
                    + Long.parseLong(expiresInSecsFromNow) * 1000L;
1101
            setAccessExpires(expires);
1102
        }
1103
    }
1104
 
1105
    /**
1106
     * This method is deprecated.  See {@link Facebook} and {@link Session} for more info.
1107
     *
1108
     * @return the String representing application ID
1109
     */
1110
    @Deprecated
1111
    public String getAppId() {
1112
        return mAppId;
1113
    }
1114
 
1115
    /**
1116
     * This method is deprecated.  See {@link Facebook} and {@link Session} for more info.
1117
     *
1118
     * @param appId the String representing the application ID
1119
     */
1120
    @Deprecated
1121
    public void setAppId(String appId) {
1122
        checkUserSession("setAppId");
1123
        synchronized (this.lock) {
1124
            mAppId = appId;
1125
            sessionInvalidated = true;
1126
        }
1127
    }
1128
 
1129
    private TokenCachingStrategy getTokenCache() {
1130
        // Intentionally not volatile/synchronized--it is okay if we race to
1131
        // create more than one of these.
1132
        if (tokenCache == null) {
1133
            tokenCache = new SetterTokenCachingStrategy();
1134
        }
1135
        return tokenCache;
1136
    }
1137
 
1138
    private static String[] stringArray(List<String> list) {
1139
        int size = (list != null) ? list.size() : 0;
1140
        String[] array = new String[size];
1141
 
1142
        if (list != null) {
1143
            for (int i = 0; i < array.length; i++) {
1144
                array[i] = list.get(i);
1145
            }
1146
        }
1147
 
1148
        return array;
1149
    }
1150
 
1151
    private static List<String> stringList(String[] array) {
1152
        if (array != null) {
1153
            return Arrays.asList(array);
1154
        } else {
1155
            return Collections.emptyList();
1156
        }
1157
    }
1158
 
1159
    private class SetterTokenCachingStrategy extends TokenCachingStrategy {
1160
 
1161
        @Override
1162
        public Bundle load() {
1163
            Bundle bundle = new Bundle();
1164
 
1165
            if (accessToken != null) {
1166
                TokenCachingStrategy.putToken(bundle, accessToken);
1167
                TokenCachingStrategy.putExpirationMilliseconds(bundle, accessExpiresMillisecondsAfterEpoch);
1168
                TokenCachingStrategy.putPermissions(bundle, stringList(pendingAuthorizationPermissions));
1169
                TokenCachingStrategy.putSource(bundle, AccessTokenSource.WEB_VIEW);
1170
                TokenCachingStrategy.putLastRefreshMilliseconds(bundle, lastAccessUpdateMillisecondsAfterEpoch);
1171
            }
1172
 
1173
            return bundle;
1174
        }
1175
 
1176
        @Override
1177
        public void save(Bundle bundle) {
1178
            accessToken = TokenCachingStrategy.getToken(bundle);
1179
            accessExpiresMillisecondsAfterEpoch = TokenCachingStrategy.getExpirationMilliseconds(bundle);
1180
            pendingAuthorizationPermissions = stringArray(TokenCachingStrategy.getPermissions(bundle));
1181
            lastAccessUpdateMillisecondsAfterEpoch = TokenCachingStrategy.getLastRefreshMilliseconds(bundle);
1182
        }
1183
 
1184
        @Override
1185
        public void clear() {
1186
            accessToken = null;
1187
        }
1188
    }
1189
 
1190
    /**
1191
     * Get Attribution ID for app install conversion tracking.
1192
     * <p/>
1193
     * This method is deprecated.  See {@link Facebook} and {@link Settings} for more info.
1194
     *
1195
     * @param contentResolver
1196
     * @return Attribution ID that will be used for conversion tracking. It will be null only if
1197
     *         the user has not installed or logged in to the Facebook app.
1198
     */
1199
    @Deprecated
1200
    public static String getAttributionId(ContentResolver contentResolver) {
1201
        return Settings.getAttributionId(contentResolver);
1202
    }
1203
 
1204
    /**
1205
     * Get the auto install publish setting.  If true, an install event will be published during authorize(), unless
1206
     * it has occurred previously or the app does not have install attribution enabled on the application's developer
1207
     * config page.
1208
     * <p/>
1209
     * This method is deprecated.  See {@link Facebook} and {@link Settings} for more info.
1210
     *
1211
     * @return a Boolean indicating whether installation of the app should be auto-published.
1212
     */
1213
    @Deprecated
1214
    public boolean getShouldAutoPublishInstall() {
1215
        return Settings.getShouldAutoPublishInstall();
1216
    }
1217
 
1218
    /**
1219
     * Sets whether auto publishing of installs will occur.
1220
     * <p/>
1221
     * This method is deprecated.  See {@link Facebook} and {@link Settings} for more info.
1222
     *
1223
     * @param value a Boolean indicating whether installation of the app should be auto-published.
1224
     */
1225
    @Deprecated
1226
    public void setShouldAutoPublishInstall(boolean value) {
1227
        Settings.setShouldAutoPublishInstall(value);
1228
    }
1229
 
1230
    /**
1231
     * Callback interface for dialog requests.
1232
     * <p/>
1233
     * THIS CLASS SHOULD BE CONSIDERED DEPRECATED.
1234
     * <p/>
1235
     * All public members of this class are intentionally deprecated.
1236
     * New code should instead use
1237
     * {@link com.facebook.widget.WebDialog}
1238
     * <p/>
1239
     * Adding @Deprecated to this class causes warnings in other deprecated classes
1240
     * that reference this one.  That is the only reason this entire class is not
1241
     * deprecated.
1242
     *
1243
     * @devDocDeprecated
1244
     */
1245
    public static interface DialogListener {
1246
 
1247
        /**
1248
         * Called when a dialog completes.
1249
         * 
1250
         * Executed by the thread that initiated the dialog.
1251
         * 
1252
         * @param values
1253
         *            Key-value string pairs extracted from the response.
1254
         */
1255
        public void onComplete(Bundle values);
1256
 
1257
        /**
1258
         * Called when a Facebook responds to a dialog with an error.
1259
         * 
1260
         * Executed by the thread that initiated the dialog.
1261
         * 
1262
         */
1263
        public void onFacebookError(FacebookError e);
1264
 
1265
        /**
1266
         * Called when a dialog has an error.
1267
         * 
1268
         * Executed by the thread that initiated the dialog.
1269
         * 
1270
         */
1271
        public void onError(DialogError e);
1272
 
1273
        /**
1274
         * Called when a dialog is canceled by the user.
1275
         * 
1276
         * Executed by the thread that initiated the dialog.
1277
         * 
1278
         */
1279
        public void onCancel();
1280
 
1281
    }
1282
 
1283
    /**
1284
     * Callback interface for service requests.
1285
     * <p/>
1286
     * THIS CLASS SHOULD BE CONSIDERED DEPRECATED.
1287
     * <p/>
1288
     * All public members of this class are intentionally deprecated.
1289
     * New code should instead use
1290
     * {@link Session} to manage session state.
1291
     * <p/>
1292
     * Adding @Deprecated to this class causes warnings in other deprecated classes
1293
     * that reference this one.  That is the only reason this entire class is not
1294
     * deprecated.
1295
     *
1296
     * @devDocDeprecated
1297
     */
1298
    public static interface ServiceListener {
1299
 
1300
        /**
1301
         * Called when a service request completes.
1302
         * 
1303
         * @param values
1304
         *            Key-value string pairs extracted from the response.
1305
         */
1306
        public void onComplete(Bundle values);
1307
 
1308
        /**
1309
         * Called when a Facebook server responds to the request with an error.
1310
         */
1311
        public void onFacebookError(FacebookError e);
1312
 
1313
        /**
1314
         * Called when a Facebook Service responds to the request with an error.
1315
         */
1316
        public void onError(Error e);
1317
 
1318
    }
1319
 
1320
    @Deprecated
1321
    public static final String FB_APP_SIGNATURE =
1322
        "30820268308201d102044a9c4610300d06092a864886f70d0101040500307a310"
1323
        + "b3009060355040613025553310b30090603550408130243413112301006035504"
1324
        + "07130950616c6f20416c746f31183016060355040a130f46616365626f6f6b204"
1325
        + "d6f62696c653111300f060355040b130846616365626f6f6b311d301b06035504"
1326
        + "03131446616365626f6f6b20436f72706f726174696f6e3020170d30393038333"
1327
        + "13231353231365a180f32303530303932353231353231365a307a310b30090603"
1328
        + "55040613025553310b30090603550408130243413112301006035504071309506"
1329
        + "16c6f20416c746f31183016060355040a130f46616365626f6f6b204d6f62696c"
1330
        + "653111300f060355040b130846616365626f6f6b311d301b06035504031314466"
1331
        + "16365626f6f6b20436f72706f726174696f6e30819f300d06092a864886f70d01"
1332
        + "0101050003818d0030818902818100c207d51df8eb8c97d93ba0c8c1002c928fa"
1333
        + "b00dc1b42fca5e66e99cc3023ed2d214d822bc59e8e35ddcf5f44c7ae8ade50d7"
1334
        + "e0c434f500e6c131f4a2834f987fc46406115de2018ebbb0d5a3c261bd97581cc"
1335
        + "fef76afc7135a6d59e8855ecd7eacc8f8737e794c60a761c536b72b11fac8e603"
1336
        + "f5da1a2d54aa103b8a13c0dbc10203010001300d06092a864886f70d010104050"
1337
        + "0038181005ee9be8bcbb250648d3b741290a82a1c9dc2e76a0af2f2228f1d9f9c"
1338
        + "4007529c446a70175c5a900d5141812866db46be6559e2141616483998211f4a6"
1339
        + "73149fb2232a10d247663b26a9031e15f84bc1c74d141ff98a02d76f85b2c8ab2"
1340
        + "571b6469b232d8e768a7f7ca04f7abe4a775615916c07940656b58717457b42bd"
1341
        + "928a2";
1342
 
1343
}