зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1351805 part 3 - Refresh the remote devices list on Married/Engaged states. r=Grisha
MozReview-Commit-ID: 1Ktbtlzc1fI --HG-- extra : rebase_source : 2d52926ee1ba8511a32b5a9cfdc13cd04ef4bbb8
This commit is contained in:
Родитель
c48ef3d269
Коммит
dd3f4bde18
|
@ -832,6 +832,7 @@ sync_java_files = [TOPSRCDIR + '/mobile/android/services/src/main/java/org/mozil
|
||||||
'fxa/authenticator/FxAccountLoginException.java',
|
'fxa/authenticator/FxAccountLoginException.java',
|
||||||
'fxa/authenticator/FxADefaultLoginStateMachineDelegate.java',
|
'fxa/authenticator/FxADefaultLoginStateMachineDelegate.java',
|
||||||
'fxa/devices/FxAccountDevice.java',
|
'fxa/devices/FxAccountDevice.java',
|
||||||
|
'fxa/devices/FxAccountDeviceListUpdater.java',
|
||||||
'fxa/devices/FxAccountDeviceRegistrator.java',
|
'fxa/devices/FxAccountDeviceRegistrator.java',
|
||||||
'fxa/FirefoxAccounts.java',
|
'fxa/FirefoxAccounts.java',
|
||||||
'fxa/FxAccountConstants.java',
|
'fxa/FxAccountConstants.java',
|
||||||
|
|
|
@ -805,16 +805,15 @@ public class AndroidFxAccount {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
private long getUserDataLong(String key, long defaultValue) {
|
||||||
private <T extends Number> T getUserDataNumber(String key, T defaultValue) {
|
|
||||||
final String numStr = accountManager.getUserData(account, key);
|
final String numStr = accountManager.getUserData(account, key);
|
||||||
if (TextUtils.isEmpty(numStr)) {
|
if (TextUtils.isEmpty(numStr)) {
|
||||||
return defaultValue;
|
return defaultValue;
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
return (T) NumberFormat.getInstance().parse(numStr);
|
return Long.parseLong(key);
|
||||||
} catch (ParseException e) {
|
} catch (NumberFormatException e) {
|
||||||
Logger.warn(LOG_TAG, "Couldn't parse " + key + "; defaulting to 0L.", e);
|
Logger.warn(LOG_TAG, "Couldn't parse " + key + "; defaulting to " + defaultValue, e);
|
||||||
return defaultValue;
|
return defaultValue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -825,19 +824,28 @@ public class AndroidFxAccount {
|
||||||
}
|
}
|
||||||
|
|
||||||
public synchronized int getDeviceRegistrationVersion() {
|
public synchronized int getDeviceRegistrationVersion() {
|
||||||
return getUserDataNumber(ACCOUNT_KEY_DEVICE_REGISTRATION_VERSION, 0);
|
final String numStr = accountManager.getUserData(account, ACCOUNT_KEY_DEVICE_REGISTRATION_VERSION);
|
||||||
|
if (TextUtils.isEmpty(numStr)) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
return Integer.parseInt(numStr);
|
||||||
|
} catch (NumberFormatException e) {
|
||||||
|
Logger.warn(LOG_TAG, "Couldn't parse ACCOUNT_KEY_DEVICE_REGISTRATION_VERSION; defaulting to 0", e);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public synchronized long getDeviceRegistrationTimestamp() {
|
public synchronized long getDeviceRegistrationTimestamp() {
|
||||||
return getUserDataNumber(ACCOUNT_KEY_DEVICE_REGISTRATION_TIMESTAMP, 0L);
|
return getUserDataLong(ACCOUNT_KEY_DEVICE_REGISTRATION_TIMESTAMP, 0L);
|
||||||
}
|
}
|
||||||
|
|
||||||
public synchronized long getDevicePushRegistrationError() {
|
public synchronized long getDevicePushRegistrationError() {
|
||||||
return getUserDataNumber(ACCOUNT_KEY_DEVICE_PUSH_REGISTRATION_ERROR, 0L);
|
return getUserDataLong(ACCOUNT_KEY_DEVICE_PUSH_REGISTRATION_ERROR, 0L);
|
||||||
}
|
}
|
||||||
|
|
||||||
public synchronized long getDevicePushRegistrationErrorTime() {
|
public synchronized long getDevicePushRegistrationErrorTime() {
|
||||||
return getUserDataNumber(ACCOUNT_KEY_DEVICE_PUSH_REGISTRATION_ERROR_TIME, 0L);
|
return getUserDataLong(ACCOUNT_KEY_DEVICE_PUSH_REGISTRATION_ERROR_TIME, 0L);
|
||||||
}
|
}
|
||||||
|
|
||||||
public synchronized void setDeviceId(String id) {
|
public synchronized void setDeviceId(String id) {
|
||||||
|
|
|
@ -8,42 +8,48 @@ import org.mozilla.gecko.sync.ExtendedJSONObject;
|
||||||
|
|
||||||
public class FxAccountDevice {
|
public class FxAccountDevice {
|
||||||
|
|
||||||
public static final String JSON_KEY_NAME = "name";
|
private static final String JSON_KEY_NAME = "name";
|
||||||
public static final String JSON_KEY_ID = "id";
|
private static final String JSON_KEY_ID = "id";
|
||||||
public static final String JSON_KEY_TYPE = "type";
|
private static final String JSON_KEY_TYPE = "type";
|
||||||
public static final String JSON_KEY_ISCURRENTDEVICE = "isCurrentDevice";
|
private static final String JSON_KEY_ISCURRENTDEVICE = "isCurrentDevice";
|
||||||
public static final String JSON_KEY_PUSH_CALLBACK = "pushCallback";
|
private static final String JSON_KEY_PUSH_CALLBACK = "pushCallback";
|
||||||
public static final String JSON_KEY_PUSH_PUBLICKEY = "pushPublicKey";
|
private static final String JSON_KEY_PUSH_PUBLICKEY = "pushPublicKey";
|
||||||
public static final String JSON_KEY_PUSH_AUTHKEY = "pushAuthKey";
|
private static final String JSON_KEY_PUSH_AUTHKEY = "pushAuthKey";
|
||||||
|
private static final String JSON_LAST_ACCESS_TIME = "lastAccessTime";
|
||||||
|
|
||||||
public final String id;
|
public final String id;
|
||||||
public final String name;
|
public final String name;
|
||||||
public final String type;
|
public final String type;
|
||||||
public final Boolean isCurrentDevice;
|
public final Boolean isCurrentDevice;
|
||||||
|
public final Long lastAccessTime;
|
||||||
public final String pushCallback;
|
public final String pushCallback;
|
||||||
public final String pushPublicKey;
|
public final String pushPublicKey;
|
||||||
public final String pushAuthKey;
|
public final String pushAuthKey;
|
||||||
|
|
||||||
public FxAccountDevice(String name, String id, String type, Boolean isCurrentDevice,
|
public FxAccountDevice(String name, String id, String type, Boolean isCurrentDevice,
|
||||||
String pushCallback, String pushPublicKey, String pushAuthKey) {
|
Long lastAccessTime, String pushCallback, String pushPublicKey,
|
||||||
|
String pushAuthKey) {
|
||||||
this.name = name;
|
this.name = name;
|
||||||
this.id = id;
|
this.id = id;
|
||||||
this.type = type;
|
this.type = type;
|
||||||
this.isCurrentDevice = isCurrentDevice;
|
this.isCurrentDevice = isCurrentDevice;
|
||||||
|
this.lastAccessTime = lastAccessTime;
|
||||||
this.pushCallback = pushCallback;
|
this.pushCallback = pushCallback;
|
||||||
this.pushPublicKey = pushPublicKey;
|
this.pushPublicKey = pushPublicKey;
|
||||||
this.pushAuthKey = pushAuthKey;
|
this.pushAuthKey = pushAuthKey;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static FxAccountDevice fromJson(ExtendedJSONObject json) {
|
public static FxAccountDevice fromJson(ExtendedJSONObject json) {
|
||||||
String name = json.getString(JSON_KEY_NAME);
|
final String name = json.getString(JSON_KEY_NAME);
|
||||||
String id = json.getString(JSON_KEY_ID);
|
final String id = json.getString(JSON_KEY_ID);
|
||||||
String type = json.getString(JSON_KEY_TYPE);
|
final String type = json.getString(JSON_KEY_TYPE);
|
||||||
Boolean isCurrentDevice = json.getBoolean(JSON_KEY_ISCURRENTDEVICE);
|
final Boolean isCurrentDevice = json.getBoolean(JSON_KEY_ISCURRENTDEVICE);
|
||||||
String pushCallback = json.getString(JSON_KEY_PUSH_CALLBACK);
|
final Long lastAccessTime = json.getLong(JSON_LAST_ACCESS_TIME);
|
||||||
String pushPublicKey = json.getString(JSON_KEY_PUSH_PUBLICKEY);
|
final String pushCallback = json.getString(JSON_KEY_PUSH_CALLBACK);
|
||||||
String pushAuthKey = json.getString(JSON_KEY_PUSH_AUTHKEY);
|
final String pushPublicKey = json.getString(JSON_KEY_PUSH_PUBLICKEY);
|
||||||
return new FxAccountDevice(name, id, type, isCurrentDevice, pushCallback, pushPublicKey, pushAuthKey);
|
final String pushAuthKey = json.getString(JSON_KEY_PUSH_AUTHKEY);
|
||||||
|
return new FxAccountDevice(name, id, type, isCurrentDevice, lastAccessTime, pushCallback,
|
||||||
|
pushPublicKey, pushAuthKey);
|
||||||
}
|
}
|
||||||
|
|
||||||
public ExtendedJSONObject toJson() {
|
public ExtendedJSONObject toJson() {
|
||||||
|
@ -73,7 +79,6 @@ public class FxAccountDevice {
|
||||||
private String id;
|
private String id;
|
||||||
private String name;
|
private String name;
|
||||||
private String type;
|
private String type;
|
||||||
private Boolean isCurrentDevice;
|
|
||||||
private String pushCallback;
|
private String pushCallback;
|
||||||
private String pushPublicKey;
|
private String pushPublicKey;
|
||||||
private String pushAuthKey;
|
private String pushAuthKey;
|
||||||
|
@ -90,10 +95,6 @@ public class FxAccountDevice {
|
||||||
this.type = type;
|
this.type = type;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void isCurrentDevice() {
|
|
||||||
this.isCurrentDevice = Boolean.TRUE;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void pushCallback(String pushCallback) {
|
public void pushCallback(String pushCallback) {
|
||||||
this.pushCallback = pushCallback;
|
this.pushCallback = pushCallback;
|
||||||
}
|
}
|
||||||
|
@ -107,7 +108,7 @@ public class FxAccountDevice {
|
||||||
}
|
}
|
||||||
|
|
||||||
public FxAccountDevice build() {
|
public FxAccountDevice build() {
|
||||||
return new FxAccountDevice(this.name, this.id, this.type, this.isCurrentDevice,
|
return new FxAccountDevice(this.name, this.id, this.type, null, null,
|
||||||
this.pushCallback, this.pushPublicKey, this.pushAuthKey);
|
this.pushCallback, this.pushPublicKey, this.pushAuthKey);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,111 @@
|
||||||
|
/* 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.devices;
|
||||||
|
|
||||||
|
import android.content.ContentResolver;
|
||||||
|
import android.content.ContentValues;
|
||||||
|
import android.net.Uri;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.support.annotation.NonNull;
|
||||||
|
import android.support.annotation.VisibleForTesting;
|
||||||
|
import android.util.Log;
|
||||||
|
|
||||||
|
import org.mozilla.gecko.background.fxa.FxAccountClient;
|
||||||
|
import org.mozilla.gecko.background.fxa.FxAccountClient20;
|
||||||
|
import org.mozilla.gecko.background.fxa.FxAccountClientException;
|
||||||
|
import org.mozilla.gecko.db.BrowserContract;
|
||||||
|
import org.mozilla.gecko.db.BrowserContract.RemoteDevices;
|
||||||
|
import org.mozilla.gecko.fxa.authenticator.AndroidFxAccount;
|
||||||
|
import org.mozilla.gecko.fxa.login.State;
|
||||||
|
|
||||||
|
import java.util.concurrent.Executor;
|
||||||
|
|
||||||
|
public class FxAccountDeviceListUpdater implements FxAccountClient20.RequestDelegate<FxAccountDevice[]> {
|
||||||
|
private static final String LOG_TAG = "FxADeviceListUpdater";
|
||||||
|
|
||||||
|
private final AndroidFxAccount fxAccount;
|
||||||
|
private final ContentResolver contentResolver;
|
||||||
|
|
||||||
|
public FxAccountDeviceListUpdater(final AndroidFxAccount fxAccount, final ContentResolver cr) {
|
||||||
|
this.fxAccount = fxAccount;
|
||||||
|
this.contentResolver = cr;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void handleSuccess(final FxAccountDevice[] result) {
|
||||||
|
final Uri uri = RemoteDevices.CONTENT_URI
|
||||||
|
.buildUpon()
|
||||||
|
.appendQueryParameter(BrowserContract.PARAM_PROFILE, fxAccount.getProfile())
|
||||||
|
.build();
|
||||||
|
|
||||||
|
final Bundle valuesBundle = new Bundle();
|
||||||
|
final ContentValues[] insertValues = new ContentValues[result.length];
|
||||||
|
|
||||||
|
final long now = System.currentTimeMillis();
|
||||||
|
for (int i = 0; i < result.length; i++) {
|
||||||
|
final FxAccountDevice fxADevice = result[i];
|
||||||
|
final ContentValues deviceValues = new ContentValues();
|
||||||
|
deviceValues.put(RemoteDevices.GUID, fxADevice.id);
|
||||||
|
deviceValues.put(RemoteDevices.TYPE, fxADevice.type);
|
||||||
|
deviceValues.put(RemoteDevices.NAME, fxADevice.name);
|
||||||
|
deviceValues.put(RemoteDevices.IS_CURRENT_DEVICE, fxADevice.isCurrentDevice);
|
||||||
|
deviceValues.put(RemoteDevices.DATE_CREATED, now);
|
||||||
|
deviceValues.put(RemoteDevices.DATE_MODIFIED, now);
|
||||||
|
// TODO: Remove that line once FxA sends lastAccessTime all the time.
|
||||||
|
final Long lastAccessTime = fxADevice.lastAccessTime != null ? fxADevice.lastAccessTime : 0;
|
||||||
|
deviceValues.put(RemoteDevices.LAST_ACCESS_TIME, lastAccessTime);
|
||||||
|
insertValues[i] = deviceValues;
|
||||||
|
}
|
||||||
|
valuesBundle.putParcelableArray(BrowserContract.METHOD_PARAM_DATA, insertValues);
|
||||||
|
try {
|
||||||
|
contentResolver.call(uri, BrowserContract.METHOD_REPLACE_REMOTE_CLIENTS, uri.toString(),
|
||||||
|
valuesBundle);
|
||||||
|
Log.i(LOG_TAG, "FxA Device list update done.");
|
||||||
|
} catch (Exception e) {
|
||||||
|
Log.e(LOG_TAG, "Error persisting the new remote device list.", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void handleError(Exception e) {
|
||||||
|
onError(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void handleFailure(FxAccountClientException.FxAccountClientRemoteException e) {
|
||||||
|
onError(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void onError(final Exception e) {
|
||||||
|
Log.e(LOG_TAG, "Error while getting the FxA device list.", e);
|
||||||
|
}
|
||||||
|
|
||||||
|
@VisibleForTesting
|
||||||
|
FxAccountClient getSynchronousFxaClient() {
|
||||||
|
return new FxAccountClient20(fxAccount.getAccountServerURI(),
|
||||||
|
// Current thread executor :)
|
||||||
|
new Executor() {
|
||||||
|
@Override
|
||||||
|
public void execute(@NonNull Runnable runnable) {
|
||||||
|
runnable.run();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void update() {
|
||||||
|
Log.i(LOG_TAG, "Beginning FxA device list update.");
|
||||||
|
final byte[] sessionToken;
|
||||||
|
try {
|
||||||
|
sessionToken = fxAccount.getState().getSessionToken();
|
||||||
|
} catch (State.NotASessionTokenState e) {
|
||||||
|
// This should never happen, because the caller (FxAccountSyncAdapter) verifies that
|
||||||
|
// we are in a token state before calling this method.
|
||||||
|
throw new IllegalStateException("Could not get a session token during Sync (?) " + e);
|
||||||
|
}
|
||||||
|
final FxAccountClient fxaClient = getSynchronousFxaClient();
|
||||||
|
fxaClient.deviceList(sessionToken, this);
|
||||||
|
}
|
||||||
|
}
|
|
@ -378,8 +378,9 @@ public class FxAccountDeviceRegistrator implements BundleEventListener {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
final FxAccountDevice updatedDevice = new FxAccountDevice(device.name, fxaDevice.id, device.type,
|
final FxAccountDevice updatedDevice = new FxAccountDevice(device.name, fxaDevice.id, device.type,
|
||||||
device.isCurrentDevice, device.pushCallback,
|
null, null,
|
||||||
device.pushPublicKey, device.pushAuthKey);
|
device.pushCallback, device.pushPublicKey,
|
||||||
|
device.pushAuthKey);
|
||||||
doFxaRegistration(context, fxAccount, updatedDevice, false);
|
doFxaRegistration(context, fxAccount, updatedDevice, false);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,8 +5,10 @@
|
||||||
package org.mozilla.gecko.fxa.receivers;
|
package org.mozilla.gecko.fxa.receivers;
|
||||||
|
|
||||||
import android.app.IntentService;
|
import android.app.IntentService;
|
||||||
|
import android.content.ContentResolver;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
|
import android.net.Uri;
|
||||||
import android.text.TextUtils;
|
import android.text.TextUtils;
|
||||||
|
|
||||||
import org.mozilla.gecko.background.common.log.Logger;
|
import org.mozilla.gecko.background.common.log.Logger;
|
||||||
|
@ -15,6 +17,7 @@ import org.mozilla.gecko.background.fxa.FxAccountClientException;
|
||||||
import org.mozilla.gecko.background.fxa.oauth.FxAccountAbstractClient;
|
import org.mozilla.gecko.background.fxa.oauth.FxAccountAbstractClient;
|
||||||
import org.mozilla.gecko.background.fxa.oauth.FxAccountAbstractClientException.FxAccountAbstractClientRemoteException;
|
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;
|
||||||
|
import org.mozilla.gecko.db.BrowserContract;
|
||||||
import org.mozilla.gecko.fxa.FxAccountConstants;
|
import org.mozilla.gecko.fxa.FxAccountConstants;
|
||||||
import org.mozilla.gecko.fxa.authenticator.AndroidFxAccount;
|
import org.mozilla.gecko.fxa.authenticator.AndroidFxAccount;
|
||||||
import org.mozilla.gecko.fxa.sync.FxAccountNotificationManager;
|
import org.mozilla.gecko.fxa.sync.FxAccountNotificationManager;
|
||||||
|
@ -75,6 +78,8 @@ public class FxAccountDeletedService extends IntentService {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
clearRemoteDevicesList(intent, context);
|
||||||
|
|
||||||
// Delete current device the from FxA devices list.
|
// Delete current device the from FxA devices list.
|
||||||
deleteFxADevice(intent);
|
deleteFxADevice(intent);
|
||||||
|
|
||||||
|
@ -159,6 +164,17 @@ public class FxAccountDeletedService extends IntentService {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void clearRemoteDevicesList(Intent intent, Context context) {
|
||||||
|
final Uri remoteDevicesUriWithProfile = BrowserContract.RemoteDevices.CONTENT_URI
|
||||||
|
.buildUpon()
|
||||||
|
.appendQueryParameter(BrowserContract.PARAM_PROFILE,
|
||||||
|
intent.getStringExtra(FxAccountConstants.ACCOUNT_DELETED_INTENT_ACCOUNT_PROFILE))
|
||||||
|
.build();
|
||||||
|
ContentResolver cr = context.getContentResolver();
|
||||||
|
|
||||||
|
cr.delete(remoteDevicesUriWithProfile, null, null);
|
||||||
|
}
|
||||||
|
|
||||||
// Remove our current device from the FxA device list.
|
// Remove our current device from the FxA device list.
|
||||||
private void deleteFxADevice(Intent intent) {
|
private void deleteFxADevice(Intent intent) {
|
||||||
final byte[] sessionToken = intent.getByteArrayExtra(FxAccountConstants.ACCOUNT_DELETED_INTENT_ACCOUNT_SESSION_TOKEN);
|
final byte[] sessionToken = intent.getByteArrayExtra(FxAccountConstants.ACCOUNT_DELETED_INTENT_ACCOUNT_SESSION_TOKEN);
|
||||||
|
|
|
@ -6,6 +6,7 @@ package org.mozilla.gecko.fxa.sync;
|
||||||
|
|
||||||
import android.accounts.Account;
|
import android.accounts.Account;
|
||||||
import android.content.AbstractThreadedSyncAdapter;
|
import android.content.AbstractThreadedSyncAdapter;
|
||||||
|
import android.content.ContentProvider;
|
||||||
import android.content.ContentProviderClient;
|
import android.content.ContentProviderClient;
|
||||||
import android.content.ContentResolver;
|
import android.content.ContentResolver;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
|
@ -22,6 +23,7 @@ import org.mozilla.gecko.background.fxa.SkewHandler;
|
||||||
import org.mozilla.gecko.browserid.JSONWebTokenUtils;
|
import org.mozilla.gecko.browserid.JSONWebTokenUtils;
|
||||||
import org.mozilla.gecko.fxa.FirefoxAccounts;
|
import org.mozilla.gecko.fxa.FirefoxAccounts;
|
||||||
import org.mozilla.gecko.fxa.FxAccountConstants;
|
import org.mozilla.gecko.fxa.FxAccountConstants;
|
||||||
|
import org.mozilla.gecko.fxa.devices.FxAccountDeviceListUpdater;
|
||||||
import org.mozilla.gecko.fxa.devices.FxAccountDeviceRegistrator;
|
import org.mozilla.gecko.fxa.devices.FxAccountDeviceRegistrator;
|
||||||
import org.mozilla.gecko.fxa.authenticator.AccountPickler;
|
import org.mozilla.gecko.fxa.authenticator.AccountPickler;
|
||||||
import org.mozilla.gecko.fxa.authenticator.AndroidFxAccount;
|
import org.mozilla.gecko.fxa.authenticator.AndroidFxAccount;
|
||||||
|
@ -418,6 +420,15 @@ public class FxAccountSyncAdapter extends AbstractThreadedSyncAdapter {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void onSessionTokenStateReached(Context context, AndroidFxAccount fxAccount) {
|
||||||
|
// This does not block the main thread, if work has to be done it is executed in a new thread.
|
||||||
|
maybeRegisterDevice(context, fxAccount);
|
||||||
|
|
||||||
|
FxAccountDeviceListUpdater deviceListUpdater = new FxAccountDeviceListUpdater(fxAccount, context.getContentResolver());
|
||||||
|
// Since the clients stage requires a fresh list of remote devices, we update the device list synchronously.
|
||||||
|
deviceListUpdater.update();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A trivial Sync implementation that does not cache client keys,
|
* A trivial Sync implementation that does not cache client keys,
|
||||||
* certificates, or tokens.
|
* certificates, or tokens.
|
||||||
|
@ -545,7 +556,7 @@ public class FxAccountSyncAdapter extends AbstractThreadedSyncAdapter {
|
||||||
schedulePolicy.onHandleFinal(notMarried.getNeededAction());
|
schedulePolicy.onHandleFinal(notMarried.getNeededAction());
|
||||||
syncDelegate.handleCannotSync(notMarried);
|
syncDelegate.handleCannotSync(notMarried);
|
||||||
if (notMarried.getStateLabel() == StateLabel.Engaged) {
|
if (notMarried.getStateLabel() == StateLabel.Engaged) {
|
||||||
maybeRegisterDevice(context, fxAccount);
|
onSessionTokenStateReached(context, fxAccount);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -588,6 +599,8 @@ public class FxAccountSyncAdapter extends AbstractThreadedSyncAdapter {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
onSessionTokenStateReached(context, fxAccount);
|
||||||
|
|
||||||
final SessionCallback sessionCallback = new SessionCallback(syncDelegate, schedulePolicy);
|
final SessionCallback sessionCallback = new SessionCallback(syncDelegate, schedulePolicy);
|
||||||
final KeyBundle syncKeyBundle = married.getSyncKeyBundle();
|
final KeyBundle syncKeyBundle = married.getSyncKeyBundle();
|
||||||
final String clientState = married.getClientState();
|
final String clientState = married.getClientState();
|
||||||
|
@ -595,8 +608,6 @@ public class FxAccountSyncAdapter extends AbstractThreadedSyncAdapter {
|
||||||
assertion, tokenServerEndpointURI, tokenBackoffHandler, sharedPrefs,
|
assertion, tokenServerEndpointURI, tokenBackoffHandler, sharedPrefs,
|
||||||
syncKeyBundle, clientState, sessionCallback, extras, fxAccount, syncDeadline);
|
syncKeyBundle, clientState, sessionCallback, extras, fxAccount, syncDeadline);
|
||||||
|
|
||||||
maybeRegisterDevice(context, fxAccount);
|
|
||||||
|
|
||||||
// Force fetch the profile avatar information. (asynchronous, in another thread)
|
// Force fetch the profile avatar information. (asynchronous, in another thread)
|
||||||
Logger.info(LOG_TAG, "Fetching profile avatar information.");
|
Logger.info(LOG_TAG, "Fetching profile avatar information.");
|
||||||
fxAccount.fetchProfileJSON();
|
fxAccount.fetchProfileJSON();
|
||||||
|
|
|
@ -0,0 +1,188 @@
|
||||||
|
/* Any copyright is dedicated to the Public Domain.
|
||||||
|
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||||
|
|
||||||
|
package org.mozilla.gecko.fxa.devices;
|
||||||
|
|
||||||
|
import android.content.ContentProviderClient;
|
||||||
|
import android.content.ContentResolver;
|
||||||
|
import android.content.ContentValues;
|
||||||
|
import android.database.Cursor;
|
||||||
|
import android.net.Uri;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.os.RemoteException;
|
||||||
|
|
||||||
|
import org.junit.Before;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.junit.runner.RunWith;
|
||||||
|
import org.mockito.ArgumentCaptor;
|
||||||
|
import org.mockito.Mock;
|
||||||
|
import org.mockito.MockitoAnnotations;
|
||||||
|
import org.mozilla.gecko.background.db.DelegatingTestContentProvider;
|
||||||
|
import org.mozilla.gecko.background.fxa.FxAccountClient;
|
||||||
|
import org.mozilla.gecko.background.testhelpers.TestRunner;
|
||||||
|
import org.mozilla.gecko.db.BrowserContract;
|
||||||
|
import org.mozilla.gecko.db.BrowserProvider;
|
||||||
|
import org.mozilla.gecko.fxa.authenticator.AndroidFxAccount;
|
||||||
|
import org.mozilla.gecko.fxa.login.State;
|
||||||
|
import org.robolectric.shadows.ShadowContentResolver;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import static java.util.Objects.deepEquals;
|
||||||
|
import static org.junit.Assert.assertEquals;
|
||||||
|
import static org.junit.Assert.assertTrue;
|
||||||
|
import static org.junit.Assert.fail;
|
||||||
|
import static org.mockito.Matchers.anyString;
|
||||||
|
import static org.mockito.Mockito.any;
|
||||||
|
import static org.mockito.Mockito.doReturn;
|
||||||
|
import static org.mockito.Mockito.eq;
|
||||||
|
import static org.mockito.Mockito.spy;
|
||||||
|
import static org.mockito.Mockito.verify;
|
||||||
|
import static org.mockito.Mockito.when;
|
||||||
|
|
||||||
|
@RunWith(TestRunner.class)
|
||||||
|
public class TestFxAccountDeviceListUpdater {
|
||||||
|
|
||||||
|
@Before
|
||||||
|
public void init() {
|
||||||
|
// Process Mockito annotations
|
||||||
|
MockitoAnnotations.initMocks(this);
|
||||||
|
fxaDevicesUpdater = spy(new FxAccountDeviceListUpdater(fxAccount, contentResolver));
|
||||||
|
}
|
||||||
|
|
||||||
|
FxAccountDeviceListUpdater fxaDevicesUpdater;
|
||||||
|
|
||||||
|
@Mock
|
||||||
|
AndroidFxAccount fxAccount;
|
||||||
|
|
||||||
|
@Mock
|
||||||
|
State state;
|
||||||
|
|
||||||
|
@Mock
|
||||||
|
ContentResolver contentResolver;
|
||||||
|
|
||||||
|
@Mock
|
||||||
|
FxAccountClient fxaClient;
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testUpdate() throws Throwable {
|
||||||
|
byte[] token = "usertoken".getBytes();
|
||||||
|
|
||||||
|
when(fxAccount.getState()).thenReturn(state);
|
||||||
|
when(state.getSessionToken()).thenReturn(token);
|
||||||
|
doReturn(fxaClient).when(fxaDevicesUpdater).getSynchronousFxaClient();
|
||||||
|
|
||||||
|
fxaDevicesUpdater.update();
|
||||||
|
verify(fxaClient).deviceList(token, fxaDevicesUpdater);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testSuccessHandler() throws Throwable {
|
||||||
|
FxAccountDevice[] result = new FxAccountDevice[2];
|
||||||
|
FxAccountDevice device1 = new FxAccountDevice("Current device", "deviceid1", "mobile", true, System.currentTimeMillis(),
|
||||||
|
"https://localhost/push/callback1", "abc123", "321cba");
|
||||||
|
FxAccountDevice device2 = new FxAccountDevice("Desktop PC", "deviceid2", "desktop", true, System.currentTimeMillis(),
|
||||||
|
"https://localhost/push/callback2", "abc123", "321cba");
|
||||||
|
result[0] = device1;
|
||||||
|
result[1] = device2;
|
||||||
|
|
||||||
|
when(fxAccount.getProfile()).thenReturn("default");
|
||||||
|
|
||||||
|
long timeBeforeCall = System.currentTimeMillis();
|
||||||
|
fxaDevicesUpdater.handleSuccess(result);
|
||||||
|
|
||||||
|
ArgumentCaptor<Bundle> captor = ArgumentCaptor.forClass(Bundle.class);
|
||||||
|
verify(contentResolver).call(any(Uri.class), eq(BrowserContract.METHOD_REPLACE_REMOTE_CLIENTS), anyString(), captor.capture());
|
||||||
|
List<Bundle> allArgs = captor.getAllValues();
|
||||||
|
assertTrue(allArgs.size() == 1);
|
||||||
|
ContentValues[] allValues = (ContentValues[]) allArgs.get(0).getParcelableArray(BrowserContract.METHOD_PARAM_DATA);
|
||||||
|
|
||||||
|
ContentValues firstDevice = allValues[0];
|
||||||
|
checkInsertDeviceContentValues(device1, firstDevice, timeBeforeCall);
|
||||||
|
ContentValues secondDevice = allValues[1];
|
||||||
|
checkInsertDeviceContentValues(device2, secondDevice, timeBeforeCall);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void checkInsertDeviceContentValues(FxAccountDevice device, ContentValues firstDevice, long timeBeforeCall) {
|
||||||
|
assertEquals(firstDevice.getAsString(BrowserContract.RemoteDevices.GUID), device.id);
|
||||||
|
assertEquals(firstDevice.getAsString(BrowserContract.RemoteDevices.TYPE), device.type);
|
||||||
|
assertEquals(firstDevice.getAsString(BrowserContract.RemoteDevices.NAME), device.name);
|
||||||
|
assertEquals(firstDevice.getAsBoolean(BrowserContract.RemoteDevices.IS_CURRENT_DEVICE), device.isCurrentDevice);
|
||||||
|
deepEquals(firstDevice.getAsString(BrowserContract.RemoteDevices.LAST_ACCESS_TIME), device.lastAccessTime);
|
||||||
|
assertTrue(firstDevice.getAsLong(BrowserContract.RemoteDevices.DATE_CREATED) < timeBeforeCall + 10000); // Give 10 secs of leeway
|
||||||
|
assertTrue(firstDevice.getAsLong(BrowserContract.RemoteDevices.DATE_MODIFIED) < timeBeforeCall + 10000);
|
||||||
|
assertEquals(firstDevice.getAsString(BrowserContract.RemoteDevices.NAME), device.name);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testBrowserProvider() {
|
||||||
|
Uri uri = testUri(BrowserContract.RemoteDevices.CONTENT_URI);
|
||||||
|
|
||||||
|
BrowserProvider provider = new BrowserProvider();
|
||||||
|
Cursor c = null;
|
||||||
|
try {
|
||||||
|
provider.onCreate();
|
||||||
|
ShadowContentResolver.registerProvider(BrowserContract.AUTHORITY, new DelegatingTestContentProvider(provider));
|
||||||
|
|
||||||
|
final ShadowContentResolver cr = new ShadowContentResolver();
|
||||||
|
ContentProviderClient remoteDevicesClient = cr.acquireContentProviderClient(BrowserContract.RemoteDevices.CONTENT_URI);
|
||||||
|
|
||||||
|
// First let's insert a client for initial state.
|
||||||
|
|
||||||
|
Bundle bundle = new Bundle();
|
||||||
|
ContentValues device1 = createMockRemoteClientValues("device1");
|
||||||
|
bundle.putParcelableArray(BrowserContract.METHOD_PARAM_DATA, new ContentValues[] { device1 });
|
||||||
|
|
||||||
|
remoteDevicesClient.call(BrowserContract.METHOD_REPLACE_REMOTE_CLIENTS, uri.toString(), bundle);
|
||||||
|
|
||||||
|
c = remoteDevicesClient.query(uri, null, null, null, "name ASC");
|
||||||
|
assertEquals(c.getCount(), 1);
|
||||||
|
c.moveToFirst();
|
||||||
|
int nameCol = c.getColumnIndexOrThrow("name");
|
||||||
|
assertEquals(c.getString(nameCol), "device1");
|
||||||
|
c.close();
|
||||||
|
|
||||||
|
// Then we replace our remote clients list with a new one.
|
||||||
|
|
||||||
|
bundle = new Bundle();
|
||||||
|
ContentValues device2 = createMockRemoteClientValues("device2");
|
||||||
|
ContentValues device3 = createMockRemoteClientValues("device3");
|
||||||
|
bundle.putParcelableArray(BrowserContract.METHOD_PARAM_DATA, new ContentValues[] { device2, device3 });
|
||||||
|
|
||||||
|
remoteDevicesClient.call(BrowserContract.METHOD_REPLACE_REMOTE_CLIENTS, uri.toString(), bundle);
|
||||||
|
|
||||||
|
c = remoteDevicesClient.query(uri, null, null, null, "name ASC");
|
||||||
|
assertEquals(c.getCount(), 2);
|
||||||
|
c.moveToFirst();
|
||||||
|
nameCol = c.getColumnIndexOrThrow("name");
|
||||||
|
assertEquals(c.getString(nameCol), "device2");
|
||||||
|
c.moveToNext();
|
||||||
|
assertEquals(c.getString(nameCol), "device3");
|
||||||
|
c.close();
|
||||||
|
} catch (RemoteException e) {
|
||||||
|
fail(e.getMessage());
|
||||||
|
} finally {
|
||||||
|
if (c != null && !c.isClosed()) {
|
||||||
|
c.close();
|
||||||
|
}
|
||||||
|
provider.shutdown();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private Uri testUri(Uri baseUri) {
|
||||||
|
return baseUri.buildUpon().appendQueryParameter(BrowserContract.PARAM_IS_TEST, "1").build();
|
||||||
|
}
|
||||||
|
|
||||||
|
private ContentValues createMockRemoteClientValues(String name) {
|
||||||
|
final long now = System.currentTimeMillis();
|
||||||
|
ContentValues cli = new ContentValues();
|
||||||
|
cli.put(BrowserContract.RemoteDevices.GUID, "R" + Math.floor(Math.random() * 10));
|
||||||
|
cli.put(BrowserContract.RemoteDevices.NAME, name);
|
||||||
|
cli.put(BrowserContract.RemoteDevices.TYPE, "mobile");
|
||||||
|
cli.put(BrowserContract.RemoteDevices.IS_CURRENT_DEVICE, false);
|
||||||
|
cli.put(BrowserContract.RemoteDevices.LAST_ACCESS_TIME, System.currentTimeMillis());
|
||||||
|
cli.put(BrowserContract.RemoteDevices.DATE_CREATED, now);
|
||||||
|
cli.put(BrowserContract.RemoteDevices.DATE_MODIFIED, now);
|
||||||
|
return cli;
|
||||||
|
}
|
||||||
|
}
|
|
@ -179,7 +179,7 @@ public class MockFxAccountClient implements FxAccountClient {
|
||||||
String deviceId = deviceToRegister.id;
|
String deviceId = deviceToRegister.id;
|
||||||
if (TextUtils.isEmpty(deviceId)) { // Create
|
if (TextUtils.isEmpty(deviceId)) { // Create
|
||||||
deviceId = UUID.randomUUID().toString();
|
deviceId = UUID.randomUUID().toString();
|
||||||
FxAccountDevice device = new FxAccountDevice(deviceToRegister.name, deviceId, deviceToRegister.type, null, null, null, null);
|
FxAccountDevice device = new FxAccountDevice(deviceToRegister.name, deviceId, deviceToRegister.type, null, null, null, null, null);
|
||||||
requestDelegate.handleSuccess(device);
|
requestDelegate.handleSuccess(device);
|
||||||
} else { // Update
|
} else { // Update
|
||||||
FxAccountDevice existingDevice = user.devices.get(deviceId);
|
FxAccountDevice existingDevice = user.devices.get(deviceId);
|
||||||
|
@ -188,8 +188,8 @@ public class MockFxAccountClient implements FxAccountClient {
|
||||||
if (!TextUtils.isEmpty(deviceToRegister.name)) {
|
if (!TextUtils.isEmpty(deviceToRegister.name)) {
|
||||||
deviceName = deviceToRegister.name;
|
deviceName = deviceToRegister.name;
|
||||||
} // We could also update the other fields..
|
} // We could also update the other fields..
|
||||||
FxAccountDevice device = new FxAccountDevice(deviceName, existingDevice.id, existingDevice.type,
|
FxAccountDevice device = new FxAccountDevice(deviceName, existingDevice.id, existingDevice.type, existingDevice.isCurrentDevice,
|
||||||
existingDevice.isCurrentDevice, existingDevice.pushCallback, existingDevice.pushPublicKey,existingDevice.pushAuthKey);
|
existingDevice.lastAccessTime, existingDevice.pushCallback, existingDevice.pushPublicKey,existingDevice.pushAuthKey);
|
||||||
requestDelegate.handleSuccess(device);
|
requestDelegate.handleSuccess(device);
|
||||||
} else { // Device unknown
|
} else { // Device unknown
|
||||||
handleFailure(requestDelegate, HttpStatus.SC_BAD_REQUEST, FxAccountRemoteError.UNKNOWN_DEVICE, "device is unknown");
|
handleFailure(requestDelegate, HttpStatus.SC_BAD_REQUEST, FxAccountRemoteError.UNKNOWN_DEVICE, "device is unknown");
|
||||||
|
|
Загрузка…
Ссылка в новой задаче