protected void refresh() {
    // refresh is called from our onResume, which can happen before the owning
    // Activity tells us about an account (via our public
    // refresh(AndroidFxAccount) method).
    if (fxAccount == null) {
      throw new IllegalArgumentException("fxAccount must not be null");
    }

    updateProfileInformation();
    updateAuthServerPreference();
    updateSyncServerPreference();

    try {
      // There are error states determined by Android, not the login state
      // machine, and we have a chance to present these states here. We handle
      // them specially, since we can't surface these states as part of syncing,
      // because they generally stop syncs from happening regularly. Right now
      // there are no such states.

      // Interrogate the Firefox Account's state.
      State state = fxAccount.getState();
      switch (state.getNeededAction()) {
        case NeedsUpgrade:
          showNeedsUpgrade();
          break;
        case NeedsPassword:
          showNeedsPassword();
          break;
        case NeedsVerification:
          showNeedsVerification();
          break;
        case NeedsFinishMigrating:
          showNeedsFinishMigrating();
          break;
        case None:
          showConnected();
          break;
      }

      // We check for the master setting last, since it is not strictly
      // necessary for the user to address this error state: it's really a
      // warning state. We surface it for the user's convenience, and to prevent
      // confused folks wondering why Sync is not working at all.
      final boolean masterSyncAutomatically = ContentResolver.getMasterSyncAutomatically();
      if (!masterSyncAutomatically) {
        showNeedsMasterSyncAutomaticallyEnabled();
        return;
      }
    } finally {
      // No matter our state, we should update the checkboxes.
      updateSelectedEngines();
    }

    final String clientName = clientsDataDelegate.getClientName();
    deviceNamePreference.setSummary(clientName);
    deviceNamePreference.setText(clientName);

    updateSyncNowPreference();
  }
 @Override
 public void handleFailure(FxAccountClientRemoteException e) {
   if (e.isUpgradeRequired()) {
     Logger.error(
         LOG_TAG,
         "Got upgrade required from remote server; transitioning Firefox Account to Doghouse state.");
     final State state = fxAccount.getState();
     fxAccount.setState(state.makeDoghouseState());
     // The status activity will say that the user needs to upgrade.
     redirectToActivity(FxAccountStatusActivity.class);
     return;
   }
   showRemoteError(e, R.string.fxaccount_update_credentials_unknown_error);
 }
 public synchronized void setState(State state) {
   if (state == null) {
     throw new IllegalArgumentException("state must not be null");
   }
   Logger.info(
       LOG_TAG,
       "Moving account named like "
           + getObfuscatedEmail()
           + " to state "
           + state.getStateLabel().toString());
   updateBundleValue(BUNDLE_KEY_STATE_LABEL, state.getStateLabel().name());
   updateBundleValue(BUNDLE_KEY_STATE, state.toJSONObject().toJSONString());
   broadcastAccountStateChangedIntent();
 }
 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);
   ContentResolver.setIsSyncable(account, BrowserContract.READING_LIST_AUTHORITY, 1);
 }
  public static AndroidFxAccount addAndroidAccount(
      Context context,
      String email,
      String profile,
      String idpServerURI,
      String tokenServerURI,
      State state,
      final int accountVersion,
      final boolean syncEnabled,
      final boolean fromPickle,
      ExtendedJSONObject bundle)
      throws UnsupportedEncodingException, GeneralSecurityException, URISyntaxException {
    if (email == null) {
      throw new IllegalArgumentException("email must not be null");
    }
    if (profile == null) {
      throw new IllegalArgumentException("profile must not be null");
    }
    if (idpServerURI == null) {
      throw new IllegalArgumentException("idpServerURI must not be null");
    }
    if (tokenServerURI == null) {
      throw new IllegalArgumentException("tokenServerURI must not be null");
    }
    if (state == null) {
      throw new IllegalArgumentException("state must not be null");
    }

    // TODO: Add migration code.
    if (accountVersion != CURRENT_ACCOUNT_VERSION) {
      throw new IllegalStateException(
          "Could not create account of version "
              + accountVersion
              + ". Current version is "
              + CURRENT_ACCOUNT_VERSION
              + ".");
    }

    // Android has internal restrictions that require all values in this
    // bundle to be strings. *sigh*
    Bundle userdata = new Bundle();
    userdata.putString(ACCOUNT_KEY_ACCOUNT_VERSION, "" + CURRENT_ACCOUNT_VERSION);
    userdata.putString(ACCOUNT_KEY_IDP_SERVER, idpServerURI);
    userdata.putString(ACCOUNT_KEY_TOKEN_SERVER, tokenServerURI);
    userdata.putString(ACCOUNT_KEY_AUDIENCE, FxAccountUtils.getAudienceForURL(tokenServerURI));
    userdata.putString(ACCOUNT_KEY_PROFILE, profile);

    if (bundle == null) {
      bundle = new ExtendedJSONObject();
      // TODO: How to upgrade?
      bundle.put(BUNDLE_KEY_BUNDLE_VERSION, CURRENT_BUNDLE_VERSION);
    }
    bundle.put(BUNDLE_KEY_STATE_LABEL, state.getStateLabel().name());
    bundle.put(BUNDLE_KEY_STATE, state.toJSONObject().toJSONString());

    userdata.putString(ACCOUNT_KEY_DESCRIPTOR, bundle.toJSONString());

    Account account = new Account(email, FxAccountConstants.ACCOUNT_TYPE);
    AccountManager accountManager = AccountManager.get(context);
    // We don't set an Android password, because we don't want to persist the
    // password (or anything else as powerful as the password). Instead, we
    // internally manage a sessionToken with a remotely owned lifecycle.
    boolean added = accountManager.addAccountExplicitly(account, null, userdata);
    if (!added) {
      return null;
    }

    // Try to work around an intermittent issue described at
    // http://stackoverflow.com/a/11698139.  What happens is that tests that
    // delete and re-create the same account frequently will find the account
    // missing all or some of the userdata bundle, possibly due to an Android
    // AccountManager caching bug.
    for (String key : userdata.keySet()) {
      accountManager.setUserData(account, key, userdata.getString(key));
    }

    AndroidFxAccount fxAccount = new AndroidFxAccount(context, account);

    if (!fromPickle) {
      fxAccount.clearSyncPrefs();
    }

    if (syncEnabled) {
      fxAccount.enableSyncing();
    } else {
      fxAccount.disableSyncing();
    }

    return fxAccount;
  }
 @Override
 public boolean onPreferenceClick(Preference preference) {
   final String key = preference.getKey();
   if ("debug_refresh".equals(key)) {
     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_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;
 }