summaryrefslogtreecommitdiffstats
path: root/mobile/android/services/src/main/java/org/mozilla/gecko/fxa/FxAccountDeviceRegistrator.java
diff options
context:
space:
mode:
Diffstat (limited to 'mobile/android/services/src/main/java/org/mozilla/gecko/fxa/FxAccountDeviceRegistrator.java')
-rw-r--r--mobile/android/services/src/main/java/org/mozilla/gecko/fxa/FxAccountDeviceRegistrator.java282
1 files changed, 0 insertions, 282 deletions
diff --git a/mobile/android/services/src/main/java/org/mozilla/gecko/fxa/FxAccountDeviceRegistrator.java b/mobile/android/services/src/main/java/org/mozilla/gecko/fxa/FxAccountDeviceRegistrator.java
deleted file mode 100644
index 66a8ad843..000000000
--- a/mobile/android/services/src/main/java/org/mozilla/gecko/fxa/FxAccountDeviceRegistrator.java
+++ /dev/null
@@ -1,282 +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;
-
-import android.content.Context;
-import android.content.Intent;
-import android.os.Bundle;
-import android.support.annotation.Nullable;
-import android.text.TextUtils;
-import android.util.Log;
-
-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.FxAccountClient20.AccountStatusResponse;
-import org.mozilla.gecko.background.fxa.FxAccountClient20.RequestDelegate;
-import org.mozilla.gecko.background.fxa.FxAccountClientException.FxAccountClientRemoteException;
-import org.mozilla.gecko.background.fxa.FxAccountRemoteError;
-import org.mozilla.gecko.fxa.authenticator.AndroidFxAccount;
-import org.mozilla.gecko.fxa.authenticator.AndroidFxAccount.InvalidFxAState;
-import org.mozilla.gecko.fxa.login.State;
-import org.mozilla.gecko.sync.SharedPreferencesClientsDataDelegate;
-import org.mozilla.gecko.util.BundleEventListener;
-import org.mozilla.gecko.util.EventCallback;
-
-import java.io.UnsupportedEncodingException;
-import java.lang.ref.WeakReference;
-import java.lang.reflect.InvocationTargetException;
-import java.lang.reflect.Method;
-import java.security.GeneralSecurityException;
-import java.util.concurrent.ExecutorService;
-import java.util.concurrent.Executors;
-
-/* This class provides a way to register the current device against FxA
- * and also stores the registration details in the Android FxAccount.
- * This should be used in a state where we possess a sessionToken, most likely the Married state.
- */
-public class FxAccountDeviceRegistrator implements BundleEventListener {
- private static final String LOG_TAG = "FxADeviceRegistrator";
-
- // The current version of the device registration, we use this to re-register
- // devices after we update what we send on device registration.
- public static final Integer DEVICE_REGISTRATION_VERSION = 2;
-
- private static FxAccountDeviceRegistrator instance;
- private final WeakReference<Context> context;
-
- private FxAccountDeviceRegistrator(Context appContext) {
- this.context = new WeakReference<Context>(appContext);
- }
-
- private static FxAccountDeviceRegistrator getInstance(Context appContext) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, IllegalAccessException {
- if (instance == null) {
- FxAccountDeviceRegistrator tempInstance = new FxAccountDeviceRegistrator(appContext);
- tempInstance.setupListeners(); // Set up listener for FxAccountPush:Subscribe:Response
- instance = tempInstance;
- }
- return instance;
- }
-
- public static void register(Context context) {
- Context appContext = context.getApplicationContext();
- try {
- getInstance(appContext).beginRegistration(appContext);
- } catch (Exception e) {
- Log.e(LOG_TAG, "Could not start FxA device registration", e);
- }
- }
-
- private void beginRegistration(Context context) {
- // Fire up gecko and send event
- // We create the Intent ourselves instead of using GeckoService.getIntentToCreateServices
- // because we can't import these modules (circular dependency between browser and services)
- final Intent geckoIntent = new Intent();
- geckoIntent.setAction("create-services");
- geckoIntent.setClassName(context, "org.mozilla.gecko.GeckoService");
- geckoIntent.putExtra("category", "android-push-service");
- geckoIntent.putExtra("data", "android-fxa-subscribe");
- final AndroidFxAccount fxAccount = AndroidFxAccount.fromContext(context);
- geckoIntent.putExtra("org.mozilla.gecko.intent.PROFILE_NAME", fxAccount.getProfile());
- context.startService(geckoIntent);
- // -> handleMessage()
- }
-
- @Override
- public void handleMessage(String event, Bundle message, EventCallback callback) {
- if ("FxAccountsPush:Subscribe:Response".equals(event)) {
- try {
- doFxaRegistration(message.getBundle("subscription"));
- } catch (InvalidFxAState e) {
- Log.d(LOG_TAG, "Invalid state when trying to register with FxA ", e);
- }
- } else {
- Log.e(LOG_TAG, "No action defined for " + event);
- }
- }
-
- private void doFxaRegistration(Bundle subscription) throws InvalidFxAState {
- final Context context = this.context.get();
- if (this.context == null) {
- throw new IllegalStateException("Application context has been gc'ed");
- }
- doFxaRegistration(context, subscription, true);
- }
-
- private static void doFxaRegistration(final Context context, final Bundle subscription, final boolean allowRecursion) throws InvalidFxAState {
- String pushCallback = subscription.getString("pushCallback");
- String pushPublicKey = subscription.getString("pushPublicKey");
- String pushAuthKey = subscription.getString("pushAuthKey");
-
- final AndroidFxAccount fxAccount = AndroidFxAccount.fromContext(context);
- if (fxAccount == null) {
- Log.e(LOG_TAG, "AndroidFxAccount is null");
- return;
- }
- final byte[] sessionToken = fxAccount.getSessionToken();
- final FxAccountDevice device;
- String deviceId = fxAccount.getDeviceId();
- String clientName = getClientName(fxAccount, context);
- if (TextUtils.isEmpty(deviceId)) {
- Log.i(LOG_TAG, "Attempting registration for a new device");
- device = FxAccountDevice.forRegister(clientName, "mobile", pushCallback, pushPublicKey, pushAuthKey);
- } else {
- Log.i(LOG_TAG, "Attempting registration for an existing device");
- Logger.pii(LOG_TAG, "Device ID: " + deviceId);
- device = FxAccountDevice.forUpdate(deviceId, clientName, pushCallback, pushPublicKey, pushAuthKey);
- }
-
- ExecutorService executor = Executors.newSingleThreadExecutor(); // Not called often, it's okay to spawn another thread
- final FxAccountClient20 fxAccountClient =
- new FxAccountClient20(fxAccount.getAccountServerURI(), executor);
- fxAccountClient.registerOrUpdateDevice(sessionToken, device, new RequestDelegate<FxAccountDevice>() {
- @Override
- public void handleError(Exception e) {
- Log.e(LOG_TAG, "Error while updating a device registration: ", e);
- }
-
- @Override
- public void handleFailure(FxAccountClientRemoteException error) {
- Log.e(LOG_TAG, "Error while updating a device registration: ", error);
- if (error.httpStatusCode == 400) {
- if (error.apiErrorNumber == FxAccountRemoteError.UNKNOWN_DEVICE) {
- recoverFromUnknownDevice(fxAccount);
- } else if (error.apiErrorNumber == FxAccountRemoteError.DEVICE_SESSION_CONFLICT) {
- recoverFromDeviceSessionConflict(error, fxAccountClient, sessionToken, fxAccount, context,
- subscription, allowRecursion);
- }
- } else
- if (error.httpStatusCode == 401
- && error.apiErrorNumber == FxAccountRemoteError.INVALID_AUTHENTICATION_TOKEN) {
- handleTokenError(error, fxAccountClient, fxAccount);
- } else {
- logErrorAndResetDeviceRegistrationVersion(error, fxAccount);
- }
- }
-
- @Override
- public void handleSuccess(FxAccountDevice result) {
- Log.i(LOG_TAG, "Device registration complete");
- Logger.pii(LOG_TAG, "Registered device ID: " + result.id);
- fxAccount.setFxAUserData(result.id, DEVICE_REGISTRATION_VERSION);
- }
- });
- }
-
- private static void logErrorAndResetDeviceRegistrationVersion(
- final FxAccountClientRemoteException error, final AndroidFxAccount fxAccount) {
- Log.e(LOG_TAG, "Device registration failed", error);
- fxAccount.resetDeviceRegistrationVersion();
- }
-
- @Nullable
- private static String getClientName(final AndroidFxAccount fxAccount, final Context context) {
- try {
- SharedPreferencesClientsDataDelegate clientsDataDelegate =
- new SharedPreferencesClientsDataDelegate(fxAccount.getSyncPrefs(), context);
- return clientsDataDelegate.getClientName();
- } catch (UnsupportedEncodingException | GeneralSecurityException e) {
- Log.e(LOG_TAG, "Unable to get client name.", e);
- return null;
- }
- }
-
- private static void handleTokenError(final FxAccountClientRemoteException error,
- final FxAccountClient fxAccountClient,
- final AndroidFxAccount fxAccount) {
- Log.i(LOG_TAG, "Recovering from invalid token error: ", error);
- logErrorAndResetDeviceRegistrationVersion(error, fxAccount);
- fxAccountClient.accountStatus(fxAccount.getState().uid,
- new RequestDelegate<AccountStatusResponse>() {
- @Override
- public void handleError(Exception e) {
- }
-
- @Override
- public void handleFailure(FxAccountClientRemoteException e) {
- }
-
- @Override
- public void handleSuccess(AccountStatusResponse result) {
- State doghouseState = fxAccount.getState().makeDoghouseState();
- if (!result.exists) {
- Log.i(LOG_TAG, "token invalidated because the account no longer exists");
- // TODO: Should be in a "I have an Android account, but the FxA is gone." State.
- // This will do for now..
- fxAccount.setState(doghouseState);
- return;
- }
- Log.e(LOG_TAG, "sessionToken invalid");
- fxAccount.setState(doghouseState);
- }
- });
- }
-
- private static void recoverFromUnknownDevice(final AndroidFxAccount fxAccount) {
- Log.i(LOG_TAG, "unknown device id, clearing the cached device id");
- fxAccount.setDeviceId(null);
- }
-
- /**
- * Will call delegate#complete in all cases
- */
- private static void recoverFromDeviceSessionConflict(final FxAccountClientRemoteException error,
- final FxAccountClient fxAccountClient,
- final byte[] sessionToken,
- final AndroidFxAccount fxAccount,
- final Context context,
- final Bundle subscription,
- final boolean allowRecursion) {
- Log.w(LOG_TAG, "device session conflict, attempting to ascertain the correct device id");
- fxAccountClient.deviceList(sessionToken, new RequestDelegate<FxAccountDevice[]>() {
- private void onError() {
- Log.e(LOG_TAG, "failed to recover from device-session conflict");
- logErrorAndResetDeviceRegistrationVersion(error, fxAccount);
- }
-
- @Override
- public void handleError(Exception e) {
- onError();
- }
-
- @Override
- public void handleFailure(FxAccountClientRemoteException e) {
- onError();
- }
-
- @Override
- public void handleSuccess(FxAccountDevice[] devices) {
- for (FxAccountDevice device : devices) {
- if (device.isCurrentDevice) {
- fxAccount.setFxAUserData(device.id, 0); // Reset device registration version
- if (!allowRecursion) {
- Log.d(LOG_TAG, "Failure to register a device on the second try");
- break;
- }
- try {
- doFxaRegistration(context, subscription, false);
- return;
- } catch (InvalidFxAState e) {
- Log.d(LOG_TAG, "Invalid state when trying to recover from a session conflict ", e);
- break;
- }
- }
- }
- onError();
- }
- });
- }
-
- private void setupListeners() throws ClassNotFoundException, NoSuchMethodException,
- InvocationTargetException, IllegalAccessException {
- // We have no choice but to use reflection here, sorry :(
- Class<?> eventDispatcher = Class.forName("org.mozilla.gecko.EventDispatcher");
- Method getInstance = eventDispatcher.getMethod("getInstance");
- Object instance = getInstance.invoke(null);
- Method registerBackgroundThreadListener = eventDispatcher.getMethod("registerBackgroundThreadListener",
- BundleEventListener.class, String[].class);
- registerBackgroundThreadListener.invoke(instance, this, new String[] { "FxAccountsPush:Subscribe:Response" });
- }
-}