author | Ryan VanderMeulen <ryanvm@gmail.com> |
Fri, 27 Mar 2015 15:41:53 -0400 | |
changeset 236309 | cb309087f978fc39dacf1e5ff565fd4c84d7dd95 |
parent 236308 | c4639723e721ca127fac6c5c0fd9828b6a9debf0 |
child 236310 | 02623719b09419865790bcf5dad10f253509372f |
push id | 57652 |
push user | philringnalda@gmail.com |
push date | Sat, 28 Mar 2015 18:56:57 +0000 |
treeherder | mozilla-inbound@87e10bac6fd1 [default view] [failures only] |
perfherder | [talos] [build metrics] [platform microbench] (compared to previous push) |
bugs | 1147473, 1148504, 1142596 |
milestone | 39.0a1 |
backs out | c4639723e721ca127fac6c5c0fd9828b6a9debf0 6c3fd5d5aa407616b09fea84a0136fcd8692501b 24bf86f4b440353e074c3e5c8323b2b218aa4b0e |
first release with | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
last release without | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
--- a/mobile/android/base/android-services.mozbuild +++ b/mobile/android/base/android-services.mozbuild @@ -863,17 +863,16 @@ sync_java_files = [ 'fxa/activities/FxAccountUpdateCredentialsActivity.java', 'fxa/activities/FxAccountVerifiedAccountActivity.java', 'fxa/authenticator/AccountPickler.java', 'fxa/authenticator/AndroidFxAccount.java', 'fxa/authenticator/FxAccountAuthenticator.java', 'fxa/authenticator/FxAccountAuthenticatorService.java', 'fxa/authenticator/FxAccountLoginDelegate.java', 'fxa/authenticator/FxAccountLoginException.java', - 'fxa/authenticator/FxADefaultLoginStateMachineDelegate.java', 'fxa/FirefoxAccounts.java', 'fxa/FxAccountConstants.java', 'fxa/login/BaseRequestDelegate.java', 'fxa/login/Cohabiting.java', 'fxa/login/Doghouse.java', 'fxa/login/Engaged.java', 'fxa/login/FxAccountLoginStateMachine.java', 'fxa/login/FxAccountLoginTransition.java', @@ -1167,17 +1166,16 @@ reading_list_service_java_files = [ 'reading/FetchSpec.java', 'reading/LocalReadingListStorage.java', 'reading/ReadingListChangeAccumulator.java', 'reading/ReadingListClient.java', 'reading/ReadingListClientContentValuesFactory.java', 'reading/ReadingListClientRecordFactory.java', 'reading/ReadingListConstants.java', 'reading/ReadingListDeleteDelegate.java', - 'reading/ReadingListInvalidAuthenticationException.java', 'reading/ReadingListRecord.java', 'reading/ReadingListRecordDelegate.java', 'reading/ReadingListRecordResponse.java', 'reading/ReadingListRecordUploadDelegate.java', 'reading/ReadingListResponse.java', 'reading/ReadingListStorage.java', 'reading/ReadingListStorageResponse.java', 'reading/ReadingListSyncAdapter.java',
--- a/mobile/android/base/background/fxa/FxAccountUtils.java +++ b/mobile/android/base/background/fxa/FxAccountUtils.java @@ -9,24 +9,26 @@ import java.math.BigInteger; import java.net.URI; import java.net.URISyntaxException; import java.security.GeneralSecurityException; import java.security.InvalidKeyException; import java.security.NoSuchAlgorithmException; import org.mozilla.gecko.AppConstants; import org.mozilla.gecko.R; +import org.mozilla.gecko.R.string; import org.mozilla.gecko.background.common.log.Logger; import org.mozilla.gecko.background.nativecode.NativeCrypto; import org.mozilla.gecko.sync.Utils; import org.mozilla.gecko.sync.crypto.HKDF; import org.mozilla.gecko.sync.crypto.KeyBundle; import org.mozilla.gecko.sync.crypto.PBKDF2; import android.content.Context; +import android.os.Build; public class FxAccountUtils { private static final String LOG_TAG = FxAccountUtils.class.getSimpleName(); public static final int SALT_LENGTH_BYTES = 32; public static final int SALT_LENGTH_HEX = 2 * SALT_LENGTH_BYTES; public static final int HASH_LENGTH_BYTES = 16;
--- a/mobile/android/base/background/fxa/oauth/FxAccountAbstractClientException.java +++ b/mobile/android/base/background/fxa/oauth/FxAccountAbstractClientException.java @@ -4,17 +4,16 @@ package org.mozilla.gecko.background.fxa.oauth; import org.mozilla.gecko.sync.ExtendedJSONObject; import org.mozilla.gecko.sync.HTTPFailureException; import org.mozilla.gecko.sync.net.SyncStorageResponse; import ch.boye.httpclientandroidlib.HttpResponse; -import ch.boye.httpclientandroidlib.HttpStatus; /** * From <a href="https://github.com/mozilla/fxa-auth-server/blob/master/docs/api.md">https://github.com/mozilla/fxa-auth-server/blob/master/docs/api.md</a>. */ public class FxAccountAbstractClientException extends Exception { private static final long serialVersionUID = 1953459541558266597L; public FxAccountAbstractClientException(String detailMessage) { @@ -47,20 +46,16 @@ public class FxAccountAbstractClientExce this.message = message; this.body = body; } @Override public String toString() { return "<FxAccountAbstractClientRemoteException " + this.httpStatusCode + " [" + this.apiErrorNumber + "]: " + this.message + ">"; } - - public boolean isInvalidAuthentication() { - return this.httpStatusCode == HttpStatus.SC_UNAUTHORIZED; - } } public static class FxAccountAbstractClientMalformedResponseException extends FxAccountAbstractClientRemoteException { private static final long serialVersionUID = 1209313149952001098L; public FxAccountAbstractClientMalformedResponseException(HttpResponse response) { super(response, 0, FxAccountOAuthRemoteError.UNKNOWN_ERROR, "Response malformed", "Response malformed", new ExtendedJSONObject()); }
--- a/mobile/android/base/fxa/FxAccountConstants.java +++ b/mobile/android/base/fxa/FxAccountConstants.java @@ -5,26 +5,21 @@ package org.mozilla.gecko.fxa; import org.mozilla.gecko.AppConstants; public class FxAccountConstants { public static final String GLOBAL_LOG_TAG = "FxAccounts"; public static final String ACCOUNT_TYPE = AppConstants.MOZ_ANDROID_SHARED_FXACCOUNT_TYPE; - // Must be a client ID allocated with "canGrant" privileges! - public static final String OAUTH_CLIENT_ID_FENNEC = "3332a18d142636cb"; - public static final String DEFAULT_AUTH_SERVER_ENDPOINT = "https://api.accounts.firefox.com/v1"; public static final String DEFAULT_TOKEN_SERVER_ENDPOINT = "https://token.services.mozilla.com/1.0/sync/1.5"; - public static final String DEFAULT_OAUTH_SERVER_ENDPOINT = "https://oauth.accounts.firefox.com/v1"; - public static final String STAGE_AUTH_SERVER_ENDPOINT = "https://stable.dev.lcip.org/auth/v1"; - public static final String STAGE_TOKEN_SERVER_ENDPOINT = "https://stable.dev.lcip.org/syncserver/token/1.0/sync/1.5"; - public static final String STAGE_OAUTH_SERVER_ENDPOINT = "https://oauth-stable.dev.lcip.org/v1"; + public static final String STAGE_AUTH_SERVER_ENDPOINT = "https://api-accounts.stage.mozaws.net/v1"; + public static final String STAGE_TOKEN_SERVER_ENDPOINT = "https://token.stage.mozaws.net/1.0/sync/1.5"; // You must be at least 13 years old, on the day of creation, to create a Firefox Account. public static final int MINIMUM_AGE_TO_CREATE_AN_ACCOUNT = 13; // You must wait 15 minutes after failing an age check before trying to create a different account. public static final long MINIMUM_TIME_TO_WAIT_AFTER_AGE_CHECK_FAILED_IN_MILLISECONDS = 15 * 60 * 1000; public static final String USER_AGENT = "Firefox-Android-FxAccounts/" + AppConstants.MOZ_APP_VERSION + " (" + AppConstants.MOZ_APP_DISPLAYNAME + ")";
--- a/mobile/android/base/fxa/activities/FxAccountStatusFragment.java +++ b/mobile/android/base/fxa/activities/FxAccountStatusFragment.java @@ -1,81 +1,77 @@ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ package org.mozilla.gecko.fxa.activities; -import java.util.Calendar; -import java.util.Date; -import java.util.GregorianCalendar; -import java.util.HashMap; -import java.util.Map; -import java.util.Set; - import org.mozilla.gecko.AppConstants; import org.mozilla.gecko.R; import org.mozilla.gecko.background.common.log.Logger; import org.mozilla.gecko.background.fxa.FxAccountUtils; import org.mozilla.gecko.background.preferences.PreferenceFragment; import org.mozilla.gecko.db.BrowserContract; import org.mozilla.gecko.fxa.FirefoxAccounts; import org.mozilla.gecko.fxa.FxAccountConstants; import org.mozilla.gecko.fxa.authenticator.AndroidFxAccount; import org.mozilla.gecko.fxa.login.Married; import org.mozilla.gecko.fxa.login.State; import org.mozilla.gecko.fxa.sync.FxAccountSyncStatusHelper; import org.mozilla.gecko.fxa.tasks.FxAccountCodeResender; -import org.mozilla.gecko.reading.ReadingListConstants; import org.mozilla.gecko.sync.ExtendedJSONObject; import org.mozilla.gecko.sync.SharedPreferencesClientsDataDelegate; import org.mozilla.gecko.sync.SyncConfiguration; import org.mozilla.gecko.util.HardwareUtils; import android.accounts.Account; -import android.accounts.AccountManager; import android.content.ContentResolver; import android.content.Context; import android.content.Intent; import android.content.SharedPreferences; import android.os.Bundle; import android.os.Handler; import android.preference.CheckBoxPreference; import android.preference.EditTextPreference; import android.preference.Preference; import android.preference.Preference.OnPreferenceChangeListener; import android.preference.Preference.OnPreferenceClickListener; import android.preference.PreferenceCategory; import android.preference.PreferenceScreen; import android.text.TextUtils; import android.text.format.DateUtils; -import android.widget.Toast; + +import java.util.Calendar; +import java.util.Date; +import java.util.GregorianCalendar; +import java.util.HashMap; +import java.util.Map; +import java.util.Set; /** * A fragment that displays the status of an AndroidFxAccount. * <p> * The owning activity is responsible for providing an AndroidFxAccount at * appropriate times. */ public class FxAccountStatusFragment extends PreferenceFragment implements OnPreferenceClickListener, OnPreferenceChangeListener { private static final String LOG_TAG = FxAccountStatusFragment.class.getSimpleName(); - /** - * If a device claims to have synced before this date, we will assume it has never synced. - */ - private static final Date EARLIEST_VALID_SYNCED_DATE; - static { - final Calendar c = GregorianCalendar.getInstance(); - c.set(2000, Calendar.JANUARY, 1, 0, 0, 0); - EARLIEST_VALID_SYNCED_DATE = c.getTime(); - } - + /** + * If a device claims to have synced before this date, we will assume it has never synced. + */ + private static final Date EARLIEST_VALID_SYNCED_DATE; + static { + final Calendar c = GregorianCalendar.getInstance(); + c.set(2000, Calendar.JANUARY, 1, 0, 0, 0); + EARLIEST_VALID_SYNCED_DATE = c.getTime(); + } // When a checkbox is toggled, wait 5 seconds (for other checkbox actions) // before trying to sync. Should we kill off the fragment before the sync // request happens, that's okay: the runnable will run if the UI thread is // still around to service it, and since we're not updating any UI, we'll just // schedule the sync as usual. See also comment below about garbage // collection. private static final long DELAY_IN_MILLISECONDS_BEFORE_REQUESTING_SYNC = 5 * 1000; private static final long LAST_SYNCED_TIME_UPDATE_INTERVAL_IN_MILLISECONDS = 60 * 1000; @@ -83,25 +79,16 @@ public class FxAccountStatusFragment // By default, the auth/account server preference is only shown when the // account is configured to use a custom server. In debug mode, this is set. private static boolean ALWAYS_SHOW_AUTH_SERVER = false; // By default, the Sync server preference is only shown when the account is // configured to use a custom Sync server. In debug mode, this is set. private static boolean ALWAYS_SHOW_SYNC_SERVER = false; - // If the user clicks the email field this many times, the debug / personal - // information logging setting will toggle. The setting is not permanent: it - // lasts until this process is killed. We don't want to dump PII to the log - // for a long time! - private final int NUMBER_OF_CLICKS_TO_TOGGLE_DEBUG = - // !defined(MOZILLA_OFFICIAL) || defined(NIGHTLY_BUILD) || defined(MOZ_DEBUG) - (!AppConstants.MOZILLA_OFFICIAL || AppConstants.NIGHTLY_BUILD || AppConstants.DEBUG_BUILD) ? 5 : -1 /* infinite */; - private int debugClickCount = 0; - protected PreferenceCategory accountCategory; protected Preference emailPreference; protected Preference authServerPreference; protected Preference needsPasswordPreference; protected Preference needsUpgradePreference; protected Preference needsVerificationPreference; protected Preference needsMasterSyncAutomaticallyEnabledPreference; @@ -185,18 +172,16 @@ public class FxAccountStatusFragment if (!FxAccountUtils.LOG_PERSONAL_INFORMATION) { removeDebugButtons(); } else { connectDebugButtons(); ALWAYS_SHOW_AUTH_SERVER = true; ALWAYS_SHOW_SYNC_SERVER = true; } - emailPreference.setOnPreferenceClickListener(this); - needsPasswordPreference.setOnPreferenceClickListener(this); needsVerificationPreference.setOnPreferenceClickListener(this); needsFinishMigratingPreference.setOnPreferenceClickListener(this); bookmarksPreference.setOnPreferenceClickListener(this); historyPreference.setOnPreferenceClickListener(this); tabsPreference.setOnPreferenceClickListener(this); passwordsPreference.setOnPreferenceClickListener(this); @@ -224,27 +209,16 @@ public class FxAccountStatusFragment */ @Override public void onResume() { super.onResume(); } @Override public boolean onPreferenceClick(Preference preference) { - if (preference == emailPreference) { - debugClickCount += 1; - if (NUMBER_OF_CLICKS_TO_TOGGLE_DEBUG > 0 && debugClickCount >= NUMBER_OF_CLICKS_TO_TOGGLE_DEBUG) { - debugClickCount = 0; - FxAccountUtils.LOG_PERSONAL_INFORMATION = !FxAccountUtils.LOG_PERSONAL_INFORMATION; - Toast.makeText(getActivity(), "Toggled logging Firefox Account personal information!", Toast.LENGTH_LONG).show(); - hardRefresh(); // Display or hide debug options. - } - return true; - } - if (preference == needsPasswordPreference) { Intent intent = new Intent(getActivity(), FxAccountUpdateCredentialsActivity.class); final Bundle extras = getExtrasForAccount(); if (extras != null) { intent.putExtras(extras); } // Per http://stackoverflow.com/a/8992365, this triggers a known bug with // the soft keyboard not being shown for the started activity. Why, Android, why? @@ -785,71 +759,42 @@ public class FxAccountStatusFragment Logger.info(LOG_TAG, "Refreshing."); refresh(); } else if ("debug_dump".equals(key)) { fxAccount.dump(); } else if ("debug_force_sync".equals(key)) { Logger.info(LOG_TAG, "Force syncing."); fxAccount.requestSync(FirefoxAccounts.FORCE); // No sense refreshing, since the sync will complete in the future. - } else if ("debug_forget_reading_list_oauth_token".equals(key)) { - final Account account = fxAccount.getAndroidAccount(); - final AccountManager accountManager = AccountManager.get(getActivity()); - final String authToken = accountManager.peekAuthToken(account, ReadingListConstants.AUTH_TOKEN_TYPE); - if (authToken != null) { - Logger.info(LOG_TAG, "Forgetting reading list oauth token: " + authToken); - accountManager.invalidateAuthToken(account.type, authToken); - } else { - Logger.warn(LOG_TAG, "No reading list oauth token to forget!"); - } } else if ("debug_forget_certificate".equals(key)) { State state = fxAccount.getState(); try { Married married = (Married) state; Logger.info(LOG_TAG, "Moving to Cohabiting state: Forgetting certificate."); fxAccount.setState(married.makeCohabitingState()); refresh(); } catch (ClassCastException e) { Logger.info(LOG_TAG, "Not in Married state; can't forget certificate."); // Ignore. } - } else if ("debug_invalidate_certificate".equals(key)) { - State state = fxAccount.getState(); - try { - Married married = (Married) state; - Logger.info(LOG_TAG, "Invalidating certificate."); - fxAccount.setState(married.makeCohabitingState().withCertificate("INVALID CERTIFICATE")); - refresh(); - } catch (ClassCastException e) { - Logger.info(LOG_TAG, "Not in Married state; can't invalidate certificate."); - // Ignore. - } } else if ("debug_require_password".equals(key)) { Logger.info(LOG_TAG, "Moving to Separated state: Forgetting password."); State state = fxAccount.getState(); fxAccount.setState(state.makeSeparatedState()); refresh(); } else if ("debug_require_upgrade".equals(key)) { Logger.info(LOG_TAG, "Moving to Doghouse state: Requiring upgrade."); State state = fxAccount.getState(); fxAccount.setState(state.makeDoghouseState()); refresh(); } else if ("debug_migrated_from_sync11".equals(key)) { Logger.info(LOG_TAG, "Moving to MigratedFromSync11 state: Requiring password."); State state = fxAccount.getState(); fxAccount.setState(state.makeMigratedFromSync11State(null)); refresh(); - } else if ("debug_make_account_stage".equals(key)) { - Logger.info(LOG_TAG, "Moving Account endpoints, in place, to stage. Deleting Sync and RL prefs and requiring password."); - fxAccount.unsafeTransitionToStageEndpoints(); - refresh(); - } else if ("debug_make_account_default".equals(key)) { - Logger.info(LOG_TAG, "Moving Account endpoints, in place, to default (production). Deleting Sync and RL prefs and requiring password."); - fxAccount.unsafeTransitionToDefaultEndpoints(); - refresh(); } else { return false; } return true; } } /** @@ -857,22 +802,30 @@ public class FxAccountStatusFragment * listener to each of them. */ protected void connectDebugButtons() { // Separate listener to really separate debug logic from main code paths. final OnPreferenceClickListener listener = new DebugPreferenceClickListener(); // We don't want to use Android resource strings for debug UI, so we just // use the keys throughout. - final PreferenceCategory debugCategory = (PreferenceCategory) ensureFindPreference("debug_category"); + final Preference debugCategory = ensureFindPreference("debug_category"); debugCategory.setTitle(debugCategory.getKey()); - for (int i = 0; i < debugCategory.getPreferenceCount(); i++) { - final Preference button = debugCategory.getPreference(i); - button.setTitle(button.getKey()); // Not very friendly, but this is for debugging only! + String[] debugKeys = new String[] { + "debug_refresh", + "debug_dump", + "debug_force_sync", + "debug_forget_certificate", + "debug_require_password", + "debug_require_upgrade", + "debug_migrated_from_sync11" }; + for (String debugKey : debugKeys) { + final Preference button = ensureFindPreference(debugKey); + button.setTitle(debugKey); // Not very friendly, but this is for debugging only! button.setOnPreferenceClickListener(listener); } } @Override public boolean onPreferenceChange(Preference preference, Object newValue) { if (preference == deviceNamePreference) { String newClientName = (String) newValue;
--- a/mobile/android/base/fxa/authenticator/AndroidFxAccount.java +++ b/mobile/android/base/fxa/authenticator/AndroidFxAccount.java @@ -8,17 +8,16 @@ import java.io.UnsupportedEncodingExcept import java.net.URISyntaxException; import java.security.GeneralSecurityException; import java.util.ArrayList; import java.util.Collections; import java.util.EnumSet; import java.util.HashMap; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.Semaphore; import org.mozilla.gecko.AppConstants; import org.mozilla.gecko.background.common.GlobalConstants; import org.mozilla.gecko.background.common.log.Logger; import org.mozilla.gecko.background.fxa.FxAccountUtils; import org.mozilla.gecko.db.BrowserContract; import org.mozilla.gecko.fxa.FirefoxAccounts; import org.mozilla.gecko.fxa.FxAccountConstants; @@ -31,17 +30,16 @@ import org.mozilla.gecko.sync.setup.Cons import android.accounts.Account; import android.accounts.AccountManager; import android.content.ContentResolver; import android.content.Context; import android.content.Intent; import android.content.SharedPreferences; import android.os.Bundle; -import android.util.Log; /** * A Firefox Account that stores its details and state as user data attached to * an Android Account instance. * <p> * Account user data is accessible only to the Android App(s) that own the * Account type. Account user data is not removed when the App's private data is * cleared. @@ -617,92 +615,9 @@ public class AndroidFxAccount { final long neverSynced = -1L; try { return getSyncPrefs().getLong(PREF_KEY_LAST_SYNCED_TIMESTAMP, neverSynced); } catch (Exception e) { Logger.warn(LOG_TAG, "Got exception getting last synced time; ignoring.", e); return neverSynced; } } - - // Debug only! This is dangerous! - public void unsafeTransitionToDefaultEndpoints() { - unsafeTransitionToStageEndpoints( - FxAccountConstants.DEFAULT_AUTH_SERVER_ENDPOINT, - FxAccountConstants.DEFAULT_TOKEN_SERVER_ENDPOINT); - } - - // Debug only! This is dangerous! - public void unsafeTransitionToStageEndpoints() { - unsafeTransitionToStageEndpoints( - FxAccountConstants.STAGE_AUTH_SERVER_ENDPOINT, - FxAccountConstants.STAGE_TOKEN_SERVER_ENDPOINT); - } - - protected void unsafeTransitionToStageEndpoints(String authServerEndpoint, String tokenServerEndpoint) { - try { - getReadingListPrefs().edit().clear().commit(); - } catch (UnsupportedEncodingException | GeneralSecurityException e) { - // Ignore. - } - try { - getSyncPrefs().edit().clear().commit(); - } catch (UnsupportedEncodingException | GeneralSecurityException e) { - // Ignore. - } - State state = getState(); - setState(state.makeSeparatedState()); - accountManager.setUserData(account, ACCOUNT_KEY_IDP_SERVER, authServerEndpoint); - accountManager.setUserData(account, ACCOUNT_KEY_TOKEN_SERVER, tokenServerEndpoint); - } - - /** - * Take the lock to own updating any Firefox Account's internal state. - * - * We use a <code>Semaphore</code> rather than a <code>ReentrantLock</code> - * because the callback that needs to release the lock may not be invoked on - * the thread that initially acquired the lock. Be aware! - */ - protected static final Semaphore sLock = new Semaphore(1, true /* fair */); - - // Which consumer took the lock? - // Synchronized by this. - protected String lockTag = null; - - // Are we locked? (It's not easy to determine who took the lock dynamically, - // so we maintain this flag internally.) - // Synchronized by this. - protected boolean locked = false; - - // Block until we can take the shared state lock. - public synchronized void acquireSharedAccountStateLock(final String tag) throws InterruptedException { - final long id = Thread.currentThread().getId(); - this.lockTag = tag; - Log.d(Logger.DEFAULT_LOG_TAG, "Thread with tag and thread id acquiring lock: " + lockTag + ", " + id + " ..."); - sLock.acquire(); - locked = true; - Log.d(Logger.DEFAULT_LOG_TAG, "Thread with tag and thread id acquiring lock: " + lockTag + ", " + id + " ... ACQUIRED"); - } - - // If we hold the shared state lock, release it. Otherwise, ignore the request. - public synchronized void releaseSharedAccountStateLock() { - final long id = Thread.currentThread().getId(); - Log.d(Logger.DEFAULT_LOG_TAG, "Thread with tag and thread id releasing lock: " + lockTag + ", " + id + " ..."); - if (locked) { - sLock.release(); - locked = false; - Log.d(Logger.DEFAULT_LOG_TAG, "Thread with tag and thread id releasing lock: " + lockTag + ", " + id + " ... RELEASED"); - } else { - Log.d(Logger.DEFAULT_LOG_TAG, "Thread with tag and thread id releasing lock: " + lockTag + ", " + id + " ... NOT LOCKED"); - } - } - - @Override - protected synchronized void finalize() { - if (locked) { - // Should never happen, but... - sLock.release(); - locked = false; - final long id = Thread.currentThread().getId(); - Log.e(Logger.DEFAULT_LOG_TAG, "Thread with tag and thread id releasing lock: " + lockTag + ", " + id + " ... RELEASED DURING FINALIZE"); - } - } }
deleted file mode 100644 --- a/mobile/android/base/fxa/authenticator/FxADefaultLoginStateMachineDelegate.java +++ /dev/null @@ -1,84 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -package org.mozilla.gecko.fxa.authenticator; - -import java.security.NoSuchAlgorithmException; -import java.util.concurrent.Executor; -import java.util.concurrent.Executors; - -import org.mozilla.gecko.background.common.log.Logger; -import org.mozilla.gecko.background.fxa.FxAccountClient; -import org.mozilla.gecko.background.fxa.FxAccountClient20; -import org.mozilla.gecko.browserid.BrowserIDKeyPair; -import org.mozilla.gecko.fxa.login.FxAccountLoginStateMachine.LoginStateMachineDelegate; -import org.mozilla.gecko.fxa.login.FxAccountLoginTransition.Transition; -import org.mozilla.gecko.fxa.login.Married; -import org.mozilla.gecko.fxa.login.State; -import org.mozilla.gecko.fxa.login.State.StateLabel; -import org.mozilla.gecko.fxa.login.StateFactory; -import org.mozilla.gecko.fxa.sync.FxAccountNotificationManager; -import org.mozilla.gecko.fxa.sync.FxAccountSyncAdapter; - -import android.content.Context; - -public abstract class FxADefaultLoginStateMachineDelegate implements LoginStateMachineDelegate { - protected final static String LOG_TAG = LoginStateMachineDelegate.class.getSimpleName(); - - protected final Context context; - protected final AndroidFxAccount fxAccount; - protected final Executor executor; - protected final FxAccountClient client; - - public FxADefaultLoginStateMachineDelegate(Context context, AndroidFxAccount fxAccount) { - this.context = context; - this.fxAccount = fxAccount; - this.executor = Executors.newSingleThreadExecutor(); - this.client = new FxAccountClient20(fxAccount.getAccountServerURI(), executor); - } - - abstract public void handleNotMarried(State notMarried); - abstract public void handleMarried(Married married); - - @Override - public FxAccountClient getClient() { - return client; - } - - @Override - public long getCertificateDurationInMilliseconds() { - return 12 * 60 * 60 * 1000; - } - - @Override - public long getAssertionDurationInMilliseconds() { - return 15 * 60 * 1000; - } - - @Override - public BrowserIDKeyPair generateKeyPair() throws NoSuchAlgorithmException { - return StateFactory.generateKeyPair(); - } - - @Override - public void handleTransition(Transition transition, State state) { - Logger.info(LOG_TAG, "handleTransition: " + transition + " to " + state.getStateLabel()); - } - - @Override - public void handleFinal(State state) { - Logger.info(LOG_TAG, "handleFinal: in " + state.getStateLabel()); - fxAccount.setState(state); - // Update any notifications displayed. - final FxAccountNotificationManager notificationManager = new FxAccountNotificationManager(FxAccountSyncAdapter.NOTIFICATION_ID); - notificationManager.update(context, fxAccount); - - if (state.getStateLabel() != StateLabel.Married) { - handleNotMarried(state); - return; - } else { - handleMarried((Married) state); - } - } -}
--- a/mobile/android/base/fxa/authenticator/FxAccountAuthenticator.java +++ b/mobile/android/base/fxa/authenticator/FxAccountAuthenticator.java @@ -1,52 +1,29 @@ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ package org.mozilla.gecko.fxa.authenticator; -import java.security.NoSuchAlgorithmException; -import java.util.concurrent.Executor; -import java.util.concurrent.Executors; - import org.mozilla.gecko.background.common.log.Logger; -import org.mozilla.gecko.background.fxa.FxAccountClient; -import org.mozilla.gecko.background.fxa.FxAccountClient20; -import org.mozilla.gecko.background.fxa.FxAccountUtils; -import org.mozilla.gecko.background.fxa.oauth.FxAccountAbstractClient.RequestDelegate; -import org.mozilla.gecko.background.fxa.oauth.FxAccountAbstractClientException.FxAccountAbstractClientRemoteException; -import org.mozilla.gecko.background.fxa.oauth.FxAccountOAuthClient10; -import org.mozilla.gecko.background.fxa.oauth.FxAccountOAuthClient10.AuthorizationResponse; -import org.mozilla.gecko.browserid.BrowserIDKeyPair; -import org.mozilla.gecko.browserid.JSONWebTokenUtils; import org.mozilla.gecko.fxa.FxAccountConstants; import org.mozilla.gecko.fxa.activities.FxAccountGetStartedActivity; -import org.mozilla.gecko.fxa.login.FxAccountLoginStateMachine; -import org.mozilla.gecko.fxa.login.FxAccountLoginStateMachine.LoginStateMachineDelegate; -import org.mozilla.gecko.fxa.login.FxAccountLoginTransition.Transition; -import org.mozilla.gecko.fxa.login.Married; -import org.mozilla.gecko.fxa.login.State; -import org.mozilla.gecko.fxa.login.State.StateLabel; -import org.mozilla.gecko.fxa.login.StateFactory; -import org.mozilla.gecko.fxa.sync.FxAccountNotificationManager; -import org.mozilla.gecko.fxa.sync.FxAccountSyncAdapter; import android.accounts.AbstractAccountAuthenticator; import android.accounts.Account; import android.accounts.AccountAuthenticatorResponse; import android.accounts.AccountManager; import android.accounts.NetworkErrorException; import android.content.Context; import android.content.Intent; import android.os.Bundle; public class FxAccountAuthenticator extends AbstractAccountAuthenticator { public static final String LOG_TAG = FxAccountAuthenticator.class.getSimpleName(); - public static final int UNKNOWN_ERROR_CODE = 999; protected final Context context; protected final AccountManager accountManager; public FxAccountAuthenticator(Context context) { super(context); this.context = context; this.accountManager = AccountManager.get(context); @@ -86,216 +63,25 @@ public class FxAccountAuthenticator exte @Override public Bundle editProperties(AccountAuthenticatorResponse response, String accountType) { Logger.debug(LOG_TAG, "editProperties"); return null; } - protected static class Responder { - final AccountAuthenticatorResponse response; - final AndroidFxAccount fxAccount; - - public Responder(AccountAuthenticatorResponse response, AndroidFxAccount fxAccount) { - this.response = response; - this.fxAccount = fxAccount; - } - - public void fail(Exception e) { - Logger.warn(LOG_TAG, "Responding with error!", e); - fxAccount.releaseSharedAccountStateLock(); - final Bundle result = new Bundle(); - result.putInt(AccountManager.KEY_ERROR_CODE, UNKNOWN_ERROR_CODE); - result.putString(AccountManager.KEY_ERROR_MESSAGE, e.toString()); - response.onResult(result); - } - - public void succeed(String authToken) { - Logger.info(LOG_TAG, "Responding with success!"); - fxAccount.releaseSharedAccountStateLock(); - final Bundle result = new Bundle(); - result.putString(AccountManager.KEY_ACCOUNT_NAME, fxAccount.account.name); - result.putString(AccountManager.KEY_ACCOUNT_TYPE, fxAccount.account.type); - result.putString(AccountManager.KEY_AUTHTOKEN, authToken); - response.onResult(result); - } - } - - public abstract static class FxADefaultLoginStateMachineDelegate implements LoginStateMachineDelegate { - protected final Context context; - protected final AndroidFxAccount fxAccount; - protected final Executor executor; - protected final FxAccountClient client; - - public FxADefaultLoginStateMachineDelegate(Context context, AndroidFxAccount fxAccount) { - this.context = context; - this.fxAccount = fxAccount; - this.executor = Executors.newSingleThreadExecutor(); - this.client = new FxAccountClient20(fxAccount.getAccountServerURI(), executor); - } - - @Override - public FxAccountClient getClient() { - return client; - } - - @Override - public long getCertificateDurationInMilliseconds() { - return 12 * 60 * 60 * 1000; - } - - @Override - public long getAssertionDurationInMilliseconds() { - return 15 * 60 * 1000; - } - - @Override - public BrowserIDKeyPair generateKeyPair() throws NoSuchAlgorithmException { - return StateFactory.generateKeyPair(); - } - - @Override - public void handleTransition(Transition transition, State state) { - Logger.info(LOG_TAG, "handleTransition: " + transition + " to " + state.getStateLabel()); - } - - abstract public void handleNotMarried(State notMarried); - abstract public void handleMarried(Married married); - - @Override - public void handleFinal(State state) { - Logger.info(LOG_TAG, "handleFinal: in " + state.getStateLabel()); - fxAccount.setState(state); - // Update any notifications displayed. - final FxAccountNotificationManager notificationManager = new FxAccountNotificationManager(FxAccountSyncAdapter.NOTIFICATION_ID); - notificationManager.update(context, fxAccount); - - if (state.getStateLabel() != StateLabel.Married) { - handleNotMarried(state); - return; - } else { - handleMarried((Married) state); - } - } - } - - protected void getOAuthToken(final AccountAuthenticatorResponse response, final AndroidFxAccount fxAccount, final String scope) throws NetworkErrorException { - Logger.info(LOG_TAG, "Fetching oauth token with scope: " + scope); - - final Responder responder = new Responder(response, fxAccount); - - final String oauthServerUri = FxAccountConstants.DEFAULT_OAUTH_SERVER_ENDPOINT; - final String audience; - try { - audience = FxAccountUtils.getAudienceForURL(oauthServerUri); // The assertion gets traded in for an oauth bearer token. - } catch (Exception e) { - Logger.warn(LOG_TAG, "Got exception fetching oauth token.", e); - responder.fail(e); - return; - } - - final FxAccountLoginStateMachine stateMachine = new FxAccountLoginStateMachine(); - - stateMachine.advance(fxAccount.getState(), StateLabel.Married, new FxADefaultLoginStateMachineDelegate(context, fxAccount) { - @Override - public void handleNotMarried(State state) { - final String message = "Cannot fetch oauth token from state: " + state.getStateLabel(); - Logger.warn(LOG_TAG, message); - responder.fail(new RuntimeException(message)); - } - - @Override - public void handleMarried(final Married married) { - final String assertion; - try { - assertion = married.generateAssertion(audience, JSONWebTokenUtils.DEFAULT_ASSERTION_ISSUER); - if (FxAccountUtils.LOG_PERSONAL_INFORMATION) { - JSONWebTokenUtils.dumpAssertion(assertion); - } - } catch (Exception e) { - Logger.warn(LOG_TAG, "Got exception fetching oauth token.", e); - responder.fail(e); - return; - } - - final FxAccountOAuthClient10 oauthClient = new FxAccountOAuthClient10(oauthServerUri, executor); - Logger.debug(LOG_TAG, "OAuth fetch for scope: " + scope); - oauthClient.authorization(FxAccountConstants.OAUTH_CLIENT_ID_FENNEC, assertion, null, scope, new RequestDelegate<FxAccountOAuthClient10.AuthorizationResponse>() { - @Override - public void handleSuccess(AuthorizationResponse result) { - Logger.debug(LOG_TAG, "OAuth success."); - FxAccountUtils.pii(LOG_TAG, "Fetched oauth token: " + result.access_token); - responder.succeed(result.access_token); - } - - @Override - public void handleFailure(FxAccountAbstractClientRemoteException e) { - Logger.error(LOG_TAG, "OAuth failure.", e); - if (e.isInvalidAuthentication()) { - // We were married, generated an assertion, and our assertion was rejected by the - // oauth client. If it's a 401, we probably have a stale certificate. If instead of - // a stale certificate we have bad credentials, the state machine will fail to sign - // our public key and drive us back to Separated. - fxAccount.setState(married.makeCohabitingState()); - } - responder.fail(e); - } - - @Override - public void handleError(Exception e) { - Logger.error(LOG_TAG, "OAuth error.", e); - responder.fail(e); - } - }); - } - }); - } - @Override public Bundle getAuthToken(final AccountAuthenticatorResponse response, final Account account, final String authTokenType, final Bundle options) throws NetworkErrorException { - Logger.debug(LOG_TAG, "getAuthToken: " + authTokenType); - - // If we have a cached authToken, hand it over. - final String cachedAuthToken = AccountManager.get(context).peekAuthToken(account, authTokenType); - if (cachedAuthToken != null && !cachedAuthToken.isEmpty()) { - Logger.info(LOG_TAG, "Return cached token."); - final Bundle result = new Bundle(); - result.putString(AccountManager.KEY_ACCOUNT_NAME, account.name); - result.putString(AccountManager.KEY_ACCOUNT_TYPE, account.type); - result.putString(AccountManager.KEY_AUTHTOKEN, cachedAuthToken); - return result; - } + Logger.debug(LOG_TAG, "getAuthToken"); - // If we're asked for an oauth::scope token, try to generate one. - final String oauthPrefix = "oauth::"; - if (authTokenType != null && authTokenType.startsWith(oauthPrefix)) { - final String scope = authTokenType.substring(oauthPrefix.length()); - final AndroidFxAccount fxAccount = new AndroidFxAccount(context, account); - try { - fxAccount.acquireSharedAccountStateLock(LOG_TAG); - } catch (InterruptedException e) { - Logger.warn(LOG_TAG, "Could not acquire account state lock; return error bundle."); - final Bundle bundle = new Bundle(); - bundle.putInt(AccountManager.KEY_ERROR_CODE, 1); - bundle.putString(AccountManager.KEY_ERROR_MESSAGE, "Could not acquire account state lock."); - return bundle; - } - getOAuthToken(response, fxAccount, scope); - return null; - } + Logger.warn(LOG_TAG, "Returning null bundle for getAuthToken."); - // Otherwise, fail. - Logger.warn(LOG_TAG, "Returning error bundle for getAuthToken with unknown token type."); - final Bundle bundle = new Bundle(); - bundle.putInt(AccountManager.KEY_ERROR_CODE, 2); - bundle.putString(AccountManager.KEY_ERROR_MESSAGE, "Unknown token type: " + authTokenType); - return bundle; + return null; } @Override public String getAuthTokenLabel(String authTokenType) { Logger.debug(LOG_TAG, "getAuthTokenLabel"); return null; }
--- a/mobile/android/base/fxa/login/Cohabiting.java +++ b/mobile/android/base/fxa/login/Cohabiting.java @@ -13,20 +13,16 @@ import org.mozilla.gecko.sync.ExtendedJS public class Cohabiting extends TokensAndKeysState { private static final String LOG_TAG = Cohabiting.class.getSimpleName(); public Cohabiting(String email, String uid, byte[] sessionToken, byte[] kA, byte[] kB, BrowserIDKeyPair keyPair) { super(StateLabel.Cohabiting, email, uid, sessionToken, kA, kB, keyPair); } - public Married withCertificate(String certificate) { - return new Married(email, uid, sessionToken, kA, kB, keyPair, certificate); - } - @Override public void execute(final ExecuteDelegate delegate) { delegate.getClient().sign(sessionToken, keyPair.getPublic().toJSONObject(), delegate.getCertificateDurationInMilliseconds(), new BaseRequestDelegate<String>(this, delegate) { @Override public void handleSuccess(String certificate) { if (FxAccountUtils.LOG_PERSONAL_INFORMATION) { try { @@ -38,13 +34,13 @@ public class Cohabiting extends TokensAn FxAccountUtils.pii(LOG_TAG, "Signature: " + c.getString("signature")); } else { FxAccountUtils.pii(LOG_TAG, "Could not parse certificate!"); } } catch (Exception e) { FxAccountUtils.pii(LOG_TAG, "Could not parse certificate!"); } } - delegate.handleTransition(new LogMessage("sign succeeded"), withCertificate(certificate)); + delegate.handleTransition(new LogMessage("sign succeeded"), new Married(email, uid, sessionToken, kA, kB, keyPair, certificate)); } }); } }
--- a/mobile/android/base/fxa/login/Married.java +++ b/mobile/android/base/fxa/login/Married.java @@ -107,12 +107,12 @@ public class Married extends TokensAndKe public String getClientState() { if (FxAccountUtils.LOG_PERSONAL_INFORMATION) { FxAccountUtils.pii(LOG_TAG, "Client state: " + this.clientState); } return this.clientState; } - public Cohabiting makeCohabitingState() { + public State makeCohabitingState() { return new Cohabiting(email, uid, sessionToken, kA, kB, keyPair); } }
--- a/mobile/android/base/fxa/receivers/FxAccountDeletedService.java +++ b/mobile/android/base/fxa/receivers/FxAccountDeletedService.java @@ -63,20 +63,16 @@ public class FxAccountDeletedService ext deletePickle(context); // Delete client database and non-local tabs. Logger.info(LOG_TAG, "Deleting the entire clients database and non-local tabs"); FennecTabsRepository.deleteNonLocalClientsAndTabs(context); // Remove any displayed notifications. new FxAccountNotificationManager(FxAccountSyncAdapter.NOTIFICATION_ID).clear(context); - - // Bug 1147275: Delete cached oauth tokens. There's no way to query all - // oauth tokens from Android, so this is tricky to do comprehensively. We - // can query, individually, for specific oauth tokens to delete, however. } public static void deletePickle(final Context context) { try { AccountPickler.deletePickle(context, FxAccountConstants.ACCOUNT_PICKLE_FILENAME); } catch (Exception e) { // This should never happen, but we really don't want to die in a background thread. Logger.warn(LOG_TAG, "Got exception deleting saved pickle file; ignoring.", e);
--- a/mobile/android/base/fxa/sync/FxAccountNotificationManager.java +++ b/mobile/android/base/fxa/sync/FxAccountNotificationManager.java @@ -60,17 +60,17 @@ public class FxAccountNotificationManage * Reflect new Firefox Account state to the notification manager: show or hide * notifications reflecting the state of a Firefox Account. * * @param context * Android context. * @param fxAccount * Firefox Account to reflect to the notification manager. */ - public void update(Context context, AndroidFxAccount fxAccount) { + protected void update(Context context, AndroidFxAccount fxAccount) { final NotificationManager notificationManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE); final State state = fxAccount.getState(); final Action action = state.getNeededAction(); if (action == Action.None) { Logger.info(LOG_TAG, "State " + state.getStateLabel() + " needs no action; cancelling any existing notification."); notificationManager.cancel(notificationId); return;
--- a/mobile/android/base/fxa/sync/FxAccountSyncAdapter.java +++ b/mobile/android/base/fxa/sync/FxAccountSyncAdapter.java @@ -1,37 +1,43 @@ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ package org.mozilla.gecko.fxa.sync; import java.net.URI; import java.net.URISyntaxException; +import java.security.NoSuchAlgorithmException; import java.util.Collection; import java.util.Collections; import java.util.EnumSet; import java.util.concurrent.CountDownLatch; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import org.mozilla.gecko.background.common.log.Logger; +import org.mozilla.gecko.background.fxa.FxAccountClient; +import org.mozilla.gecko.background.fxa.FxAccountClient20; import org.mozilla.gecko.background.fxa.FxAccountUtils; import org.mozilla.gecko.background.fxa.SkewHandler; +import org.mozilla.gecko.browserid.BrowserIDKeyPair; import org.mozilla.gecko.browserid.JSONWebTokenUtils; import org.mozilla.gecko.fxa.FirefoxAccounts; import org.mozilla.gecko.fxa.FxAccountConstants; import org.mozilla.gecko.fxa.authenticator.AccountPickler; import org.mozilla.gecko.fxa.authenticator.AndroidFxAccount; -import org.mozilla.gecko.fxa.authenticator.FxADefaultLoginStateMachineDelegate; import org.mozilla.gecko.fxa.authenticator.FxAccountAuthenticator; import org.mozilla.gecko.fxa.login.FxAccountLoginStateMachine; +import org.mozilla.gecko.fxa.login.FxAccountLoginStateMachine.LoginStateMachineDelegate; +import org.mozilla.gecko.fxa.login.FxAccountLoginTransition.Transition; import org.mozilla.gecko.fxa.login.Married; import org.mozilla.gecko.fxa.login.State; import org.mozilla.gecko.fxa.login.State.StateLabel; +import org.mozilla.gecko.fxa.login.StateFactory; import org.mozilla.gecko.sync.BackoffHandler; import org.mozilla.gecko.sync.GlobalSession; import org.mozilla.gecko.sync.PrefsBackoffHandler; import org.mozilla.gecko.sync.SharedPreferencesClientsDataDelegate; import org.mozilla.gecko.sync.SyncConfiguration; import org.mozilla.gecko.sync.ThreadPool; import org.mozilla.gecko.sync.Utils; import org.mozilla.gecko.sync.crypto.KeyBundle; @@ -110,17 +116,17 @@ public class FxAccountSyncAdapter extend @Override public void rejectSync() { super.rejectSync(); } protected final Collection<String> stageNamesToSync; public SyncDelegate(CountDownLatch latch, SyncResult syncResult, AndroidFxAccount fxAccount, Collection<String> stageNamesToSync) { - super(latch, syncResult); + super(latch, syncResult, fxAccount); this.stageNamesToSync = Collections.unmodifiableCollection(stageNamesToSync); } public Collection<String> getStageNamesToSync() { return this.stageNamesToSync; } } @@ -229,30 +235,28 @@ public class FxAccountSyncAdapter extend protected void syncWithAssertion(final String audience, final String assertion, final URI tokenServerEndpointURI, final BackoffHandler tokenBackoffHandler, final SharedPreferences sharedPrefs, final KeyBundle syncKeyBundle, final String clientState, final SessionCallback callback, - final Bundle extras, - final AndroidFxAccount fxAccount) { + final Bundle extras) { final TokenServerClientDelegate delegate = new TokenServerClientDelegate() { private boolean didReceiveBackoff = false; @Override public String getUserAgent() { return FxAccountConstants.USER_AGENT; } @Override public void handleSuccess(final TokenServerToken token) { FxAccountUtils.pii(LOG_TAG, "Got token! uid is " + token.uid + " and endpoint is " + token.endpoint + "."); - fxAccount.releaseSharedAccountStateLock(); if (!didReceiveBackoff) { // We must be OK to touch this token server. tokenBackoffHandler.setEarliestNextRequest(0L); } final URI storageServerURI; try { @@ -320,34 +324,22 @@ public class FxAccountSyncAdapter extend } catch (Exception e) { callback.handleError(globalSession, e); return; } } @Override public void handleFailure(TokenServerException e) { - Logger.error(LOG_TAG, "Failed to get token.", e); - try { - // We should only get here *after* we're locked into the married state. - State state = fxAccount.getState(); - if (state.getStateLabel() == StateLabel.Married) { - Married married = (Married) state; - fxAccount.setState(married.makeCohabitingState()); - } - } finally { - fxAccount.releaseSharedAccountStateLock(); - } - callback.handleError(null, e); + handleError(e); } @Override public void handleError(Exception e) { Logger.error(LOG_TAG, "Failed to get token.", e); - fxAccount.releaseSharedAccountStateLock(); callback.handleError(null, e); } @Override public void handleBackoff(int backoffSeconds) { // This is the token server telling us to back off. Logger.info(LOG_TAG, "Token server requesting backoff of " + backoffSeconds + "s. Backoff handler: " + tokenBackoffHandler); didReceiveBackoff = true; @@ -419,16 +411,24 @@ public class FxAccountSyncAdapter extend final CountDownLatch latch = new CountDownLatch(1); Collection<String> knownStageNames = SyncConfiguration.validEngineNames(); Collection<String> stageNamesToSync = Utils.getStagesToSyncFromBundle(knownStageNames, extras); final SyncDelegate syncDelegate = new SyncDelegate(latch, syncResult, fxAccount, stageNamesToSync); try { + final State state; + try { + state = fxAccount.getState(); + } catch (Exception e) { + syncDelegate.handleError(e); + return; + } + // This will be the same chunk of SharedPreferences that we pass through to GlobalSession/SyncConfiguration. final SharedPreferences sharedPrefs = fxAccount.getSyncPrefs(); final BackoffHandler backgroundBackoffHandler = new PrefsBackoffHandler(sharedPrefs, "background"); final BackoffHandler rateLimitBackoffHandler = new PrefsBackoffHandler(sharedPrefs, "rate"); // If this sync was triggered by user action, this will be true. final boolean isImmediate = (extras != null) && @@ -451,57 +451,67 @@ public class FxAccountSyncAdapter extend } final SchedulePolicy schedulePolicy = new FxAccountSchedulePolicy(context, fxAccount); // Set a small scheduled 'backoff' to rate-limit the next sync, // and extend the background delay even further into the future. schedulePolicy.configureBackoffMillisBeforeSyncing(rateLimitBackoffHandler, backgroundBackoffHandler); + final String authServerEndpoint = fxAccount.getAccountServerURI(); final String tokenServerEndpoint = fxAccount.getTokenServerURI(); final URI tokenServerEndpointURI = new URI(tokenServerEndpoint); final String audience = FxAccountUtils.getAudienceForURL(tokenServerEndpoint); - try { - // The clock starts... now! - fxAccount.acquireSharedAccountStateLock(FxAccountSyncAdapter.LOG_TAG); - } catch (InterruptedException e) { - // OK, skip this sync. - syncDelegate.handleError(e); - return; - } + // TODO: why doesn't the loginPolicy extract the audience from the account? + final FxAccountClient client = new FxAccountClient20(authServerEndpoint, executor); + final FxAccountLoginStateMachine stateMachine = new FxAccountLoginStateMachine(); + stateMachine.advance(state, StateLabel.Married, new LoginStateMachineDelegate() { + @Override + public FxAccountClient getClient() { + return client; + } + + @Override + public long getCertificateDurationInMilliseconds() { + return 12 * 60 * 60 * 1000; + } - final State state; - try { - state = fxAccount.getState(); - } catch (Exception e) { - fxAccount.releaseSharedAccountStateLock(); - syncDelegate.handleError(e); - return; - } + @Override + public long getAssertionDurationInMilliseconds() { + return 15 * 60 * 1000; + } - final FxAccountLoginStateMachine stateMachine = new FxAccountLoginStateMachine(); - stateMachine.advance(state, StateLabel.Married, new FxADefaultLoginStateMachineDelegate(context, fxAccount) { @Override - public void handleNotMarried(State notMarried) { - Logger.info(LOG_TAG, "handleNotMarried: in " + notMarried.getStateLabel()); - schedulePolicy.onHandleFinal(notMarried.getNeededAction()); - syncDelegate.handleCannotSync(notMarried); + public BrowserIDKeyPair generateKeyPair() throws NoSuchAlgorithmException { + return StateFactory.generateKeyPair(); + } + + @Override + public void handleTransition(Transition transition, State state) { + Logger.info(LOG_TAG, "handleTransition: " + transition + " to " + state.getStateLabel()); } private boolean shouldRequestToken(final BackoffHandler tokenBackoffHandler, final Bundle extras) { return shouldPerformSync(tokenBackoffHandler, "token", extras); } @Override - public void handleMarried(Married married) { - schedulePolicy.onHandleFinal(married.getNeededAction()); - Logger.info(LOG_TAG, "handleMarried: in " + married.getStateLabel()); + public void handleFinal(State state) { + Logger.info(LOG_TAG, "handleFinal: in " + state.getStateLabel()); + fxAccount.setState(state); + schedulePolicy.onHandleFinal(state.getNeededAction()); + notificationManager.update(context, fxAccount); + try { + if (state.getStateLabel() != StateLabel.Married) { + syncDelegate.handleCannotSync(state); + return; + } - try { + final Married married = (Married) state; final String assertion = married.generateAssertion(audience, JSONWebTokenUtils.DEFAULT_ASSERTION_ISSUER); /* * At this point we're in the correct state to sync, and we're ready to fetch * a token and do some work. * * But first we need to do two things: * 1. Check to see whether we're in a backoff situation for the token server. @@ -524,28 +534,26 @@ public class FxAccountSyncAdapter extend Logger.info(LOG_TAG, "Not syncing (token server)."); syncDelegate.postponeSync(tokenBackoffHandler.delayMilliseconds()); return; } final SessionCallback sessionCallback = new SessionCallback(syncDelegate, schedulePolicy); final KeyBundle syncKeyBundle = married.getSyncKeyBundle(); final String clientState = married.getClientState(); - syncWithAssertion(audience, assertion, tokenServerEndpointURI, tokenBackoffHandler, sharedPrefs, syncKeyBundle, clientState, sessionCallback, extras, fxAccount); + syncWithAssertion(audience, assertion, tokenServerEndpointURI, tokenBackoffHandler, sharedPrefs, syncKeyBundle, clientState, sessionCallback, extras); } catch (Exception e) { syncDelegate.handleError(e); return; } } }); latch.await(); } catch (Exception e) { Logger.error(LOG_TAG, "Got error syncing.", e); syncDelegate.handleError(e); - } finally { - fxAccount.releaseSharedAccountStateLock(); } Logger.info(LOG_TAG, "Syncing done."); lastSyncRealtimeMillis = SystemClock.elapsedRealtime(); } }
--- a/mobile/android/base/fxa/sync/FxAccountSyncDelegate.java +++ b/mobile/android/base/fxa/sync/FxAccountSyncDelegate.java @@ -1,33 +1,42 @@ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ package org.mozilla.gecko.fxa.sync; import java.util.concurrent.CountDownLatch; +import org.mozilla.gecko.fxa.authenticator.AndroidFxAccount; +import org.mozilla.gecko.fxa.login.Married; import org.mozilla.gecko.fxa.login.State; +import org.mozilla.gecko.fxa.login.State.StateLabel; +import org.mozilla.gecko.tokenserver.TokenServerException; import android.content.SyncResult; public class FxAccountSyncDelegate { protected final CountDownLatch latch; protected final SyncResult syncResult; + protected final AndroidFxAccount fxAccount; - public FxAccountSyncDelegate(CountDownLatch latch, SyncResult syncResult) { + public FxAccountSyncDelegate(CountDownLatch latch, SyncResult syncResult, AndroidFxAccount fxAccount) { if (latch == null) { throw new IllegalArgumentException("latch must not be null"); } if (syncResult == null) { throw new IllegalArgumentException("syncResult must not be null"); } + if (fxAccount == null) { + throw new IllegalArgumentException("fxAccount must not be null"); + } this.latch = latch; this.syncResult = syncResult; + this.fxAccount = fxAccount; } /** * No error! Say that we made progress. */ protected void setSyncResultSuccess() { syncResult.stats.numUpdates += 1; } @@ -51,16 +60,26 @@ public class FxAccountSyncDelegate { public void handleSuccess() { setSyncResultSuccess(); latch.countDown(); } public void handleError(Exception e) { setSyncResultSoftError(); + // This is awful, but we need to propagate bad assertions back up the + // chain somehow, and this will do for now. + if (e instanceof TokenServerException) { + // We should only get here *after* we're locked into the married state. + State state = fxAccount.getState(); + if (state.getStateLabel() == StateLabel.Married) { + Married married = (Married) state; + fxAccount.setState(married.makeCohabitingState()); + } + } latch.countDown(); } /** * When the login machine terminates, we might not be in the * <code>Married</code> state, and therefore we can't sync. This method * messages as much to the user. * <p>
--- a/mobile/android/base/reading/ReadingListConstants.java +++ b/mobile/android/base/reading/ReadingListConstants.java @@ -7,13 +7,12 @@ package org.mozilla.gecko.reading; import org.mozilla.gecko.AppConstants; public class ReadingListConstants { public static final String GLOBAL_LOG_TAG = "FxReadingList"; public static final String USER_AGENT = "Firefox-Android-FxReader/" + AppConstants.MOZ_APP_VERSION + " (" + AppConstants.MOZ_APP_DISPLAYNAME + ")"; public static final String DEFAULT_DEV_ENDPOINT = "https://readinglist.dev.mozaws.net/v1/"; public static final String DEFAULT_PROD_ENDPOINT = "https://readinglist.services.mozilla.com/v1/"; - public static final String OAUTH_SCOPE_READINGLIST = "readinglist"; - public static final String AUTH_TOKEN_TYPE = "oauth::" + OAUTH_SCOPE_READINGLIST; + public static final String OAUTH_ENDPOINT_PROD = "https://oauth.accounts.firefox.com/v1"; public static boolean DEBUG = false; }
deleted file mode 100644 --- a/mobile/android/base/reading/ReadingListInvalidAuthenticationException.java +++ /dev/null @@ -1,18 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -package org.mozilla.gecko.reading; - -import org.mozilla.gecko.sync.net.MozResponse; - -public class ReadingListInvalidAuthenticationException extends Exception { - private static final long serialVersionUID = 7112459541558266597L; - - public final MozResponse response; - - public ReadingListInvalidAuthenticationException(MozResponse response) { - super(); - this.response = response; - } -}
--- a/mobile/android/base/reading/ReadingListSyncAdapter.java +++ b/mobile/android/base/reading/ReadingListSyncAdapter.java @@ -1,74 +1,87 @@ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ package org.mozilla.gecko.reading; import java.net.URI; import java.net.URISyntaxException; +import java.security.NoSuchAlgorithmException; import java.util.Collection; import java.util.concurrent.CountDownLatch; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.TimeUnit; import org.mozilla.gecko.background.common.PrefsBranch; import org.mozilla.gecko.background.common.log.Logger; +import org.mozilla.gecko.background.fxa.FxAccountClient; +import org.mozilla.gecko.background.fxa.FxAccountClient20; import org.mozilla.gecko.background.fxa.FxAccountUtils; +import org.mozilla.gecko.background.fxa.oauth.FxAccountAbstractClient.RequestDelegate; +import org.mozilla.gecko.background.fxa.oauth.FxAccountAbstractClientException.FxAccountAbstractClientRemoteException; +import org.mozilla.gecko.background.fxa.oauth.FxAccountOAuthClient10; +import org.mozilla.gecko.background.fxa.oauth.FxAccountOAuthClient10.AuthorizationResponse; +import org.mozilla.gecko.browserid.BrowserIDKeyPair; +import org.mozilla.gecko.browserid.JSONWebTokenUtils; import org.mozilla.gecko.db.BrowserContract.ReadingListItems; import org.mozilla.gecko.fxa.authenticator.AndroidFxAccount; +import org.mozilla.gecko.fxa.login.FxAccountLoginStateMachine; +import org.mozilla.gecko.fxa.login.FxAccountLoginStateMachine.LoginStateMachineDelegate; +import org.mozilla.gecko.fxa.login.FxAccountLoginTransition.Transition; +import org.mozilla.gecko.fxa.login.Married; +import org.mozilla.gecko.fxa.login.State; +import org.mozilla.gecko.fxa.login.State.StateLabel; +import org.mozilla.gecko.fxa.login.StateFactory; import org.mozilla.gecko.fxa.sync.FxAccountSyncDelegate; import org.mozilla.gecko.sync.net.AuthHeaderProvider; import org.mozilla.gecko.sync.net.BearerAuthHeaderProvider; import android.accounts.Account; -import android.accounts.AccountManager; import android.content.AbstractThreadedSyncAdapter; import android.content.ContentProviderClient; import android.content.ContentResolver; import android.content.Context; import android.content.SharedPreferences; import android.content.SyncResult; import android.os.Bundle; public class ReadingListSyncAdapter extends AbstractThreadedSyncAdapter { public static final String PREF_LOCAL_NAME = "device.localname"; + public static final String OAUTH_CLIENT_ID_FENNEC = "3332a18d142636cb"; + public static final String OAUTH_SCOPE_READINGLIST = "readinglist"; private static final String LOG_TAG = ReadingListSyncAdapter.class.getSimpleName(); private static final long TIMEOUT_SECONDS = 60; protected final ExecutorService executor; public ReadingListSyncAdapter(Context context, boolean autoInitialize) { super(context, autoInitialize); this.executor = Executors.newSingleThreadExecutor(); } - protected static abstract class SyncAdapterSynchronizerDelegate implements ReadingListSynchronizerDelegate { + + static final class SyncAdapterSynchronizerDelegate implements ReadingListSynchronizerDelegate { private final FxAccountSyncDelegate syncDelegate; private final ContentProviderClient cpc; private final SyncResult result; SyncAdapterSynchronizerDelegate(FxAccountSyncDelegate syncDelegate, ContentProviderClient cpc, SyncResult result) { this.syncDelegate = syncDelegate; this.cpc = cpc; this.result = result; } - abstract public void onInvalidAuthentication(); - @Override public void onUnableToSync(Exception e) { Logger.warn(LOG_TAG, "Unable to sync.", e); - if (e instanceof ReadingListInvalidAuthenticationException) { - onInvalidAuthentication(); - } cpc.release(); syncDelegate.handleError(e); } @Override public void onDeletionsUploadComplete() { Logger.debug(LOG_TAG, "Step: onDeletionsUploadComplete"); this.result.stats.numEntries += 1; // TODO: Bug 1140809. @@ -103,98 +116,168 @@ public class ReadingListSyncAdapter exte @Override public void onComplete() { Logger.info(LOG_TAG, "Reading list synchronization complete."); cpc.release(); syncDelegate.handleSuccess(); } } - private void syncWithAuthorization(final Context context, - final Account account, - final SyncResult syncResult, - final FxAccountSyncDelegate syncDelegate, - final String authToken, - final SharedPreferences sharedPrefs, - final Bundle extras) { - final AuthHeaderProvider auth = new BearerAuthHeaderProvider(authToken); - - final String endpointString = ReadingListConstants.DEFAULT_PROD_ENDPOINT; - final URI endpoint; - Logger.info(LOG_TAG, "Syncing reading list against " + endpointString); - try { - endpoint = new URI(endpointString); - } catch (URISyntaxException e) { - // Should never happen. - Logger.error(LOG_TAG, "Unexpected malformed URI for reading list service: " + endpointString); - syncDelegate.handleError(e); - return; - } - - final PrefsBranch branch = new PrefsBranch(sharedPrefs, "readinglist."); - final ReadingListClient remote = new ReadingListClient(endpoint, auth); - final ContentProviderClient cpc = getContentProviderClient(context); // Released by the inner SyncAdapterSynchronizerDelegate. - - final LocalReadingListStorage local = new LocalReadingListStorage(cpc); - String localName = branch.getString(PREF_LOCAL_NAME, null); - if (localName == null) { - localName = FxAccountUtils.defaultClientName(context); - } - - // Make sure DB rows don't refer to placeholder values. - local.updateLocalNames(localName); - - final ReadingListSynchronizer synchronizer = new ReadingListSynchronizer(branch, remote, local); - - synchronizer.syncAll(new SyncAdapterSynchronizerDelegate(syncDelegate, cpc, syncResult) { - @Override - public void onInvalidAuthentication() { - // The reading list server rejected our oauth token! Invalidate it. Next - // time through, we'll request a new one, which will drive the login - // state machine, produce a new assertion, and eventually a fresh token. - Logger.info(LOG_TAG, "Invalidating oauth token after 401!"); - AccountManager.get(context).invalidateAuthToken(account.type, authToken); - } - }); - // TODO: backoffs, and everything else handled by a SessionCallback. - } - @Override public void onPerformSync(final Account account, final Bundle extras, final String authority, final ContentProviderClient provider, final SyncResult syncResult) { Logger.setThreadLogTag(ReadingListConstants.GLOBAL_LOG_TAG); Logger.resetLogging(); final Context context = getContext(); final AndroidFxAccount fxAccount = new AndroidFxAccount(context, account); + // If this sync was triggered by user action, this will be true. + final boolean isImmediate = (extras != null) && + (extras.getBoolean(ContentResolver.SYNC_EXTRAS_UPLOAD, false) || + extras.getBoolean(ContentResolver.SYNC_EXTRAS_MANUAL, false)); + final CountDownLatch latch = new CountDownLatch(1); - final FxAccountSyncDelegate syncDelegate = new FxAccountSyncDelegate(latch, syncResult); + final FxAccountSyncDelegate syncDelegate = new FxAccountSyncDelegate(latch, syncResult, fxAccount); + try { + final State state; + try { + state = fxAccount.getState(); + } catch (Exception e) { + Logger.error(LOG_TAG, "Unable to sync.", e); + return; + } + + final String oauthServerUri = ReadingListConstants.OAUTH_ENDPOINT_PROD; + final String authServerEndpoint = fxAccount.getAccountServerURI(); + final String audience = FxAccountUtils.getAudienceForURL(oauthServerUri); // The assertion gets traded in for an oauth bearer token. + + final SharedPreferences sharedPrefs = fxAccount.getReadingListPrefs(); + final FxAccountClient client = new FxAccountClient20(authServerEndpoint, executor); + final FxAccountLoginStateMachine stateMachine = new FxAccountLoginStateMachine(); + + stateMachine.advance(state, StateLabel.Married, new LoginStateMachineDelegate() { + @Override + public FxAccountClient getClient() { + return client; + } + + @Override + public long getCertificateDurationInMilliseconds() { + return 12 * 60 * 60 * 1000; + } + + @Override + public long getAssertionDurationInMilliseconds() { + return 15 * 60 * 1000; + } + + @Override + public BrowserIDKeyPair generateKeyPair() throws NoSuchAlgorithmException { + return StateFactory.generateKeyPair(); + } + + @Override + public void handleTransition(Transition transition, State state) { + Logger.info(LOG_TAG, "handleTransition: " + transition + " to " + state.getStateLabel()); + } + + @Override + public void handleFinal(State state) { + Logger.info(LOG_TAG, "handleFinal: in " + state.getStateLabel()); + fxAccount.setState(state); + + // TODO: scheduling, notifications. + try { + if (state.getStateLabel() != StateLabel.Married) { + syncDelegate.handleCannotSync(state); + return; + } + + final Married married = (Married) state; + final String assertion = married.generateAssertion(audience, JSONWebTokenUtils.DEFAULT_ASSERTION_ISSUER); + JSONWebTokenUtils.dumpAssertion(assertion); - final AccountManager accountManager = AccountManager.get(context); - // If we have an auth failure that requires user intervention, FxA will show system - // notifications prompting the user to re-connect as it advances the internal account state. - // true causes the auth token fetch to return null on failure immediately, rather than doing - // Mysterious Internal Work to try to get the token. - final boolean notifyAuthFailure = true; - try { - final String authToken = accountManager.blockingGetAuthToken(account, ReadingListConstants.AUTH_TOKEN_TYPE, notifyAuthFailure); - if (authToken == null) { - throw new RuntimeException("Couldn't get oauth token! Aborting sync."); - } - final SharedPreferences sharedPrefs = fxAccount.getReadingListPrefs(); - syncWithAuthorization(context, account, syncResult, syncDelegate, authToken, sharedPrefs, extras); + final String clientID = OAUTH_CLIENT_ID_FENNEC; + final String scope = OAUTH_SCOPE_READINGLIST; + syncWithAssertion(clientID, scope, assertion, sharedPrefs, extras); + } catch (Exception e) { + syncDelegate.handleError(e); + return; + } + } + + private void syncWithAssertion(final String client_id, final String scope, final String assertion, + final SharedPreferences sharedPrefs, final Bundle extras) { + final FxAccountOAuthClient10 oauthClient = new FxAccountOAuthClient10(oauthServerUri, executor); + Logger.debug(LOG_TAG, "OAuth fetch."); + oauthClient.authorization(client_id, assertion, null, scope, new RequestDelegate<FxAccountOAuthClient10.AuthorizationResponse>() { + @Override + public void handleSuccess(AuthorizationResponse result) { + Logger.debug(LOG_TAG, "OAuth success."); + syncWithAuthorization(result, sharedPrefs, extras); + } + + @Override + public void handleFailure(FxAccountAbstractClientRemoteException e) { + Logger.error(LOG_TAG, "OAuth failure.", e); + syncDelegate.handleError(e); + } + + @Override + public void handleError(Exception e) { + Logger.error(LOG_TAG, "OAuth error.", e); + syncDelegate.handleError(e); + } + }); + } + + private void syncWithAuthorization(AuthorizationResponse authResponse, + SharedPreferences sharedPrefs, + Bundle extras) { + final AuthHeaderProvider auth = new BearerAuthHeaderProvider(authResponse.access_token); + + final String endpointString = ReadingListConstants.DEFAULT_DEV_ENDPOINT; + final URI endpoint; + Logger.info(LOG_TAG, "XXX Syncing to " + endpointString); + try { + endpoint = new URI(endpointString); + } catch (URISyntaxException e) { + // Should never happen. + Logger.error(LOG_TAG, "Unexpected malformed URI for reading list service: " + endpointString); + syncDelegate.handleError(e); + return; + } + + final PrefsBranch branch = new PrefsBranch(sharedPrefs, "readinglist."); + final ReadingListClient remote = new ReadingListClient(endpoint, auth); + final ContentProviderClient cpc = getContentProviderClient(context); // TODO: make sure I'm always released! + + final LocalReadingListStorage local = new LocalReadingListStorage(cpc); + String localName = branch.getString(PREF_LOCAL_NAME, null); + if (localName == null) { + localName = FxAccountUtils.defaultClientName(context); + } + + // Make sure DB rows don't refer to placeholder values. + local.updateLocalNames(localName); + + final ReadingListSynchronizer synchronizer = new ReadingListSynchronizer(branch, remote, local); + + synchronizer.syncAll(new SyncAdapterSynchronizerDelegate(syncDelegate, cpc, syncResult)); + // TODO: backoffs, and everything else handled by a SessionCallback. + } + }); latch.await(TIMEOUT_SECONDS, TimeUnit.SECONDS); Logger.info(LOG_TAG, "Reading list sync done."); + } catch (Exception e) { - // We can get lots of exceptions here; handle them uniformly. Logger.error(LOG_TAG, "Got error syncing.", e); syncDelegate.handleError(e); } - /* * TODO: * * Account error notifications. How do we avoid these overlapping with Sync? * * Pickling. How do we avoid pickling twice if you use both Sync and RL? */ /* * TODO: @@ -203,14 +286,15 @@ public class ReadingListSyncAdapter exte * * Syncing. * * Error handling. * * Backoff and retry-after. * * Sync scheduling. * * Forcing syncs/interactive use. */ } + private ContentProviderClient getContentProviderClient(Context context) { final ContentResolver contentResolver = context.getContentResolver(); final ContentProviderClient client = contentResolver.acquireContentProviderClient(ReadingListItems.CONTENT_URI); return client; } }
--- a/mobile/android/base/reading/ReadingListSynchronizer.java +++ b/mobile/android/base/reading/ReadingListSynchronizer.java @@ -769,21 +769,17 @@ public class ReadingListSynchronizer { delegate.fail(error); } @Override public void onFailure(MozResponse response) { final int statusCode = response.getStatusCode(); Logger.error(LOG_TAG, "Download failed. since = " + since + ". Response: " + statusCode); response.logResponseBody(LOG_TAG); - if (response.isInvalidAuthentication()) { - delegate.fail(new ReadingListInvalidAuthenticationException(response)); - } else { - delegate.fail(); - } + delegate.fail(); } @Override public void onComplete(ReadingListResponse response) { long lastModified = response.getLastModified(); Logger.info(LOG_TAG, "Server last modified: " + lastModified); try { postDownload.finish();
--- a/mobile/android/base/resources/xml/fxaccount_status_prefscreen.xml +++ b/mobile/android/base/resources/xml/fxaccount_status_prefscreen.xml @@ -121,19 +121,15 @@ android:targetPackage="@string/browser_intent_package" /> </Preference> </PreferenceCategory> <PreferenceCategory android:key="debug_category" > <Preference android:key="debug_refresh" /> <Preference android:key="debug_dump" /> <Preference android:key="debug_force_sync" /> - <Preference android:key="debug_invalidate_certificate" /> - <Preference android:key="debug_forget_reading_list_oauth_token" /> <Preference android:key="debug_forget_certificate" /> <Preference android:key="debug_require_password" /> <Preference android:key="debug_require_upgrade" /> <Preference android:key="debug_migrated_from_sync11" /> - <Preference android:key="debug_make_account_stage" /> - <Preference android:key="debug_make_account_default" /> - </PreferenceCategory> + </PreferenceCategory> </PreferenceScreen>
--- a/mobile/android/base/sync/net/MozResponse.java +++ b/mobile/android/base/sync/net/MozResponse.java @@ -14,17 +14,16 @@ import java.util.Scanner; import org.json.simple.parser.ParseException; import org.mozilla.gecko.background.common.log.Logger; import org.mozilla.gecko.sync.ExtendedJSONObject; import org.mozilla.gecko.sync.NonObjectJSONException; import ch.boye.httpclientandroidlib.Header; import ch.boye.httpclientandroidlib.HttpEntity; import ch.boye.httpclientandroidlib.HttpResponse; -import ch.boye.httpclientandroidlib.HttpStatus; import ch.boye.httpclientandroidlib.impl.cookie.DateParseException; import ch.boye.httpclientandroidlib.impl.cookie.DateUtils; public class MozResponse { private static final String LOG_TAG = "MozResponse"; private static final String HEADER_RETRY_AFTER = "retry-after"; @@ -38,20 +37,16 @@ public class MozResponse { public int getStatusCode() { return this.response.getStatusLine().getStatusCode(); } public boolean wasSuccessful() { return this.getStatusCode() == 200; } - public boolean isInvalidAuthentication() { - return this.getStatusCode() == HttpStatus.SC_UNAUTHORIZED; - } - /** * Fetch the content type of the HTTP response body. * * @return a <code>Header</code> instance, or <code>null</code> if there was * no body or no valid Content-Type. */ public Header getContentType() { HttpEntity entity = this.response.getEntity();