protected void linkifyOldFirefoxLink() { TextView oldFirefox = (TextView) findViewById(R.id.old_firefox); String text = getResources().getString(R.string.fxaccount_getting_started_old_firefox); final String url = FirefoxAccounts.getOldSyncUpgradeURL(getResources(), Locale.getDefault()); FxAccountUtils.pii( LOG_TAG, "Old Firefox url is: " + url); // Don't want to leak locale in particular. ActivityUtils.linkTextView(oldFirefox, text, url); }
protected void updateFromIntentExtras() { // Only set email/password in onCreate; we don't want to overwrite edited values onResume. if (getIntent() != null && getIntent().getExtras() != null) { Bundle bundle = getIntent().getExtras(); emailEdit.setText(bundle.getString(EXTRA_EMAIL)); passwordEdit.setText(bundle.getString(EXTRA_PASSWORD)); setPasswordButtonShown(bundle.getBoolean(EXTRA_PASSWORD_SHOWN, false)); } // This sets defaults as well as extracting from extras, so it's not conditional. updateServersFromIntentExtras(getIntent()); if (FxAccountUtils.LOG_PERSONAL_INFORMATION) { FxAccountUtils.pii(LOG_TAG, "Using auth server: " + authServerEndpoint); FxAccountUtils.pii(LOG_TAG, "Using sync server: " + syncServerEndpoint); } updateCustomServerView(); }
/** <b>For debugging only!</b> */ public void dump() { if (!FxAccountUtils.LOG_PERSONAL_INFORMATION) { return; } ExtendedJSONObject o = toJSONObject(); ArrayList<String> list = new ArrayList<String>(o.keySet()); Collections.sort(list); for (String key : list) { FxAccountUtils.pii(LOG_TAG, key + ": " + o.get(key)); } }
@Override public void handleSuccess(LoginResponse result) { Logger.info(LOG_TAG, "Got success signing in."); if (fxAccount == null) { this.handleError(new IllegalStateException("fxAccount must not be null")); return; } byte[] unwrapkB; try { // It is crucial that we use the email address provided by the server // (rather than whatever the user entered), because the user's keys are // wrapped and salted with the initial email they provided to // /create/account. Of course, we want to pass through what the user // entered locally as much as possible. byte[] quickStretchedPW = passwordStretcher.getQuickStretchedPW(result.remoteEmail.getBytes("UTF-8")); unwrapkB = FxAccountUtils.generateUnwrapBKey(quickStretchedPW); } catch (Exception e) { this.handleError(e); return; } fxAccount.setState( new Engaged( email, result.uid, result.verified, unwrapkB, result.sessionToken, result.keyFetchToken)); fxAccount.requestSync(FirefoxAccounts.FORCE); // For great debugging. if (FxAccountUtils.LOG_PERSONAL_INFORMATION) { fxAccount.dump(); } setResult(RESULT_OK); // Maybe show success activity. final Intent successIntent = makeSuccessIntent(email, result); if (successIntent != null) { startActivity(successIntent); } finish(); TelemetryWrapper.addToHistogram(TelemetryContract.SYNC11_MIGRATIONS_COMPLETED, 1); }
/** * Update profile information from json on UI thread. * * @param profileJSON json fetched from server. */ protected void updateProfileInformation(final ExtendedJSONObject profileJSON) { // View changes must always be done on UI thread. ThreadUtils.assertOnUiThread(); FxAccountUtils.pii(LOG_TAG, "Profile JSON is: " + profileJSON.toJSONString()); final String userName = profileJSON.getString(FxAccountConstants.KEY_PROFILE_JSON_USERNAME); // Update the profile username and email if available. if (!TextUtils.isEmpty(userName)) { profilePreference.setTitle(userName); profilePreference.setSummary(fxAccount.getEmail()); } else { profilePreference.setTitle(fxAccount.getEmail()); } // Icon update from java is not supported prior to API 11, skip the avatar image fetch and // update for older device. if (!AppConstants.Versions.feature11Plus) { Logger.info(LOG_TAG, "Skipping profile image fetch for older pre-API 11 devices."); return; } // Avatar URI empty, skip profile image fetch. final String avatarURI = profileJSON.getString(FxAccountConstants.KEY_PROFILE_JSON_AVATAR); if (TextUtils.isEmpty(avatarURI)) { Logger.info(LOG_TAG, "AvatarURI is empty, skipping profile image fetch."); return; } // Using noPlaceholder would avoid a pop of the default image, but it's not available in the // version of Picasso // we ship in the tree. Picasso.with(getActivity()) .load(avatarURI) .centerInside() .resizeDimen(R.dimen.fxaccount_profile_image_width, R.dimen.fxaccount_profile_image_height) .placeholder(R.drawable.sync_avatar_default) .error(R.drawable.sync_avatar_default) .into(profileAvatarTarget); }
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 void handleSuccess(LoginResponse result) { Logger.info(LOG_TAG, "Got success response; adding Android account."); // We're on the UI thread, but it's okay to create the account here. AndroidFxAccount fxAccount; try { final String profile = Constants.DEFAULT_PROFILE; final String tokenServerURI = getTokenServerEndpoint(); // It is crucial that we use the email address provided by the server // (rather than whatever the user entered), because the user's keys are // wrapped and salted with the initial email they provided to // /create/account. Of course, we want to pass through what the user // entered locally as much as possible, so we create the Android account // with their entered email address, etc. // The passwordStretcher should have seen this email address before, so // we shouldn't be calculating the expensive stretch twice. byte[] quickStretchedPW = passwordStretcher.getQuickStretchedPW(result.remoteEmail.getBytes("UTF-8")); byte[] unwrapkB = FxAccountUtils.generateUnwrapBKey(quickStretchedPW); State state = new Engaged( email, result.uid, result.verified, unwrapkB, result.sessionToken, result.keyFetchToken); fxAccount = AndroidFxAccount.addAndroidAccount( getApplicationContext(), email, profile, serverURI, tokenServerURI, state); if (fxAccount == null) { throw new RuntimeException("Could not add Android account."); } if (selectedEngines != null) { Logger.info(LOG_TAG, "User has selected engines; storing to prefs."); SyncConfiguration.storeSelectedEnginesToPrefs(fxAccount.getSyncPrefs(), selectedEngines); } } catch (Exception e) { handleError(e); return; } // For great debugging. if (FxAccountUtils.LOG_PERSONAL_INFORMATION) { fxAccount.dump(); } // The GetStarted activity has called us and needs to return a result to the authenticator. final Intent intent = new Intent(); intent.putExtra(AccountManager.KEY_ACCOUNT_NAME, email); intent.putExtra(AccountManager.KEY_ACCOUNT_TYPE, FxAccountConstants.ACCOUNT_TYPE); // intent.putExtra(AccountManager.KEY_AUTHTOKEN, accountType); setResult(RESULT_OK, intent); // Show success activity depending on verification status. Intent successIntent = makeSuccessIntent(email, result); startActivity(successIntent); finish(); }