mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-10-17 23:35:34 +00:00
Bug 1359279 - Renew GCM token/Push registration/FxA Registration on push registration expired. r=Grisha
MozReview-Commit-ID: HFDjBBt9CBA --HG-- extra : rebase_source : 727caefdd3ad0fcb9383f2098a8651a6124f662f
This commit is contained in:
parent
89a09e8d67
commit
126bd9114d
@ -15,7 +15,8 @@ public class FxAccountDevice {
|
|||||||
private static final String JSON_KEY_PUSH_CALLBACK = "pushCallback";
|
private static final String JSON_KEY_PUSH_CALLBACK = "pushCallback";
|
||||||
private static final String JSON_KEY_PUSH_PUBLICKEY = "pushPublicKey";
|
private static final String JSON_KEY_PUSH_PUBLICKEY = "pushPublicKey";
|
||||||
private 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";
|
private static final String JSON_KEY_LAST_ACCESS_TIME = "lastAccessTime";
|
||||||
|
private static final String JSON_KEY_PUSH_ENDPOINT_EXPIRED = "pushEndpointExpired";
|
||||||
|
|
||||||
public final String id;
|
public final String id;
|
||||||
public final String name;
|
public final String name;
|
||||||
@ -25,10 +26,11 @@ public class FxAccountDevice {
|
|||||||
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 final Boolean pushEndpointExpired;
|
||||||
|
|
||||||
public FxAccountDevice(String name, String id, String type, Boolean isCurrentDevice,
|
public FxAccountDevice(String name, String id, String type, Boolean isCurrentDevice,
|
||||||
Long lastAccessTime, String pushCallback, String pushPublicKey,
|
Long lastAccessTime, String pushCallback, String pushPublicKey,
|
||||||
String pushAuthKey) {
|
String pushAuthKey, Boolean pushEndpointExpired) {
|
||||||
this.name = name;
|
this.name = name;
|
||||||
this.id = id;
|
this.id = id;
|
||||||
this.type = type;
|
this.type = type;
|
||||||
@ -37,6 +39,7 @@ public class FxAccountDevice {
|
|||||||
this.pushCallback = pushCallback;
|
this.pushCallback = pushCallback;
|
||||||
this.pushPublicKey = pushPublicKey;
|
this.pushPublicKey = pushPublicKey;
|
||||||
this.pushAuthKey = pushAuthKey;
|
this.pushAuthKey = pushAuthKey;
|
||||||
|
this.pushEndpointExpired = pushEndpointExpired;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static FxAccountDevice fromJson(ExtendedJSONObject json) {
|
public static FxAccountDevice fromJson(ExtendedJSONObject json) {
|
||||||
@ -44,12 +47,24 @@ public class FxAccountDevice {
|
|||||||
final String id = json.getString(JSON_KEY_ID);
|
final String id = json.getString(JSON_KEY_ID);
|
||||||
final String type = json.getString(JSON_KEY_TYPE);
|
final String type = json.getString(JSON_KEY_TYPE);
|
||||||
final Boolean isCurrentDevice = json.getBoolean(JSON_KEY_ISCURRENTDEVICE);
|
final Boolean isCurrentDevice = json.getBoolean(JSON_KEY_ISCURRENTDEVICE);
|
||||||
final Long lastAccessTime = json.getLong(JSON_LAST_ACCESS_TIME);
|
final Long lastAccessTime = json.getLong(JSON_KEY_LAST_ACCESS_TIME);
|
||||||
final String pushCallback = json.getString(JSON_KEY_PUSH_CALLBACK);
|
final String pushCallback = json.getString(JSON_KEY_PUSH_CALLBACK);
|
||||||
final String pushPublicKey = json.getString(JSON_KEY_PUSH_PUBLICKEY);
|
final String pushPublicKey = json.getString(JSON_KEY_PUSH_PUBLICKEY);
|
||||||
final String pushAuthKey = json.getString(JSON_KEY_PUSH_AUTHKEY);
|
final String pushAuthKey = json.getString(JSON_KEY_PUSH_AUTHKEY);
|
||||||
|
// The FxA server sends this boolean as a number (bug):
|
||||||
|
// https://github.com/mozilla/fxa-auth-server/pull/2122
|
||||||
|
// Use getBoolean directly once the fix is deployed (probably ~Oct-Nov 2017).
|
||||||
|
final Object pushEndpointExpiredRaw = json.get(JSON_KEY_PUSH_ENDPOINT_EXPIRED);
|
||||||
|
final Boolean pushEndpointExpired;
|
||||||
|
if (pushEndpointExpiredRaw instanceof Number) {
|
||||||
|
pushEndpointExpired = ((Number) pushEndpointExpiredRaw).intValue() == 1;
|
||||||
|
} else if (pushEndpointExpiredRaw instanceof Boolean) {
|
||||||
|
pushEndpointExpired = (Boolean) pushEndpointExpiredRaw;
|
||||||
|
} else {
|
||||||
|
pushEndpointExpired = false;
|
||||||
|
}
|
||||||
return new FxAccountDevice(name, id, type, isCurrentDevice, lastAccessTime, pushCallback,
|
return new FxAccountDevice(name, id, type, isCurrentDevice, lastAccessTime, pushCallback,
|
||||||
pushPublicKey, pushAuthKey);
|
pushPublicKey, pushAuthKey, pushEndpointExpired);
|
||||||
}
|
}
|
||||||
|
|
||||||
public ExtendedJSONObject toJson() {
|
public ExtendedJSONObject toJson() {
|
||||||
@ -109,7 +124,7 @@ public class FxAccountDevice {
|
|||||||
|
|
||||||
public FxAccountDevice build() {
|
public FxAccountDevice build() {
|
||||||
return new FxAccountDevice(this.name, this.id, this.type, null, null,
|
return new FxAccountDevice(this.name, this.id, this.type, null, null,
|
||||||
this.pushCallback, this.pushPublicKey, this.pushAuthKey);
|
this.pushCallback, this.pushPublicKey, this.pushAuthKey, null);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -6,6 +6,8 @@ package org.mozilla.gecko.fxa.devices;
|
|||||||
|
|
||||||
import android.content.ContentResolver;
|
import android.content.ContentResolver;
|
||||||
import android.content.ContentValues;
|
import android.content.ContentValues;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.SharedPreferences;
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.support.annotation.NonNull;
|
import android.support.annotation.NonNull;
|
||||||
@ -19,7 +21,12 @@ import org.mozilla.gecko.db.BrowserContract;
|
|||||||
import org.mozilla.gecko.db.BrowserContract.RemoteDevices;
|
import org.mozilla.gecko.db.BrowserContract.RemoteDevices;
|
||||||
import org.mozilla.gecko.fxa.authenticator.AndroidFxAccount;
|
import org.mozilla.gecko.fxa.authenticator.AndroidFxAccount;
|
||||||
import org.mozilla.gecko.fxa.login.State;
|
import org.mozilla.gecko.fxa.login.State;
|
||||||
|
import org.mozilla.gecko.util.ThreadUtils;
|
||||||
|
|
||||||
|
import java.io.UnsupportedEncodingException;
|
||||||
|
import java.lang.reflect.InvocationTargetException;
|
||||||
|
import java.lang.reflect.Method;
|
||||||
|
import java.security.GeneralSecurityException;
|
||||||
import java.util.concurrent.Executor;
|
import java.util.concurrent.Executor;
|
||||||
|
|
||||||
public class FxAccountDeviceListUpdater implements FxAccountClient20.RequestDelegate<FxAccountDevice[]> {
|
public class FxAccountDeviceListUpdater implements FxAccountClient20.RequestDelegate<FxAccountDevice[]> {
|
||||||
@ -27,6 +34,10 @@ public class FxAccountDeviceListUpdater implements FxAccountClient20.RequestDele
|
|||||||
|
|
||||||
private final AndroidFxAccount fxAccount;
|
private final AndroidFxAccount fxAccount;
|
||||||
private final ContentResolver contentResolver;
|
private final ContentResolver contentResolver;
|
||||||
|
private boolean localDevicePushEndpointExpired = false;
|
||||||
|
|
||||||
|
private final static String SYNC_PREFS_PUSH_LAST_RENEW_REGISTRATION_MS = "push.lastRenewRegistration";
|
||||||
|
private final static long TIME_BETWEEN_RENEW_REGISTRATION_MS = 2 * 7 * 24 * 3600 * 1000;
|
||||||
|
|
||||||
public FxAccountDeviceListUpdater(final AndroidFxAccount fxAccount, final ContentResolver cr) {
|
public FxAccountDeviceListUpdater(final AndroidFxAccount fxAccount, final ContentResolver cr) {
|
||||||
this.fxAccount = fxAccount;
|
this.fxAccount = fxAccount;
|
||||||
@ -46,6 +57,9 @@ public class FxAccountDeviceListUpdater implements FxAccountClient20.RequestDele
|
|||||||
final long now = System.currentTimeMillis();
|
final long now = System.currentTimeMillis();
|
||||||
for (int i = 0; i < result.length; i++) {
|
for (int i = 0; i < result.length; i++) {
|
||||||
final FxAccountDevice fxADevice = result[i];
|
final FxAccountDevice fxADevice = result[i];
|
||||||
|
if (fxADevice.isCurrentDevice && fxADevice.pushEndpointExpired) {
|
||||||
|
this.localDevicePushEndpointExpired = true;
|
||||||
|
}
|
||||||
final ContentValues deviceValues = new ContentValues();
|
final ContentValues deviceValues = new ContentValues();
|
||||||
deviceValues.put(RemoteDevices.GUID, fxADevice.id);
|
deviceValues.put(RemoteDevices.GUID, fxADevice.id);
|
||||||
deviceValues.put(RemoteDevices.TYPE, fxADevice.type);
|
deviceValues.put(RemoteDevices.TYPE, fxADevice.type);
|
||||||
@ -53,9 +67,7 @@ public class FxAccountDeviceListUpdater implements FxAccountClient20.RequestDele
|
|||||||
deviceValues.put(RemoteDevices.IS_CURRENT_DEVICE, fxADevice.isCurrentDevice);
|
deviceValues.put(RemoteDevices.IS_CURRENT_DEVICE, fxADevice.isCurrentDevice);
|
||||||
deviceValues.put(RemoteDevices.DATE_CREATED, now);
|
deviceValues.put(RemoteDevices.DATE_CREATED, now);
|
||||||
deviceValues.put(RemoteDevices.DATE_MODIFIED, now);
|
deviceValues.put(RemoteDevices.DATE_MODIFIED, now);
|
||||||
// TODO: Remove that line once FxA sends lastAccessTime all the time.
|
deviceValues.put(RemoteDevices.LAST_ACCESS_TIME, fxADevice.lastAccessTime);
|
||||||
final Long lastAccessTime = fxADevice.lastAccessTime != null ? fxADevice.lastAccessTime : 0;
|
|
||||||
deviceValues.put(RemoteDevices.LAST_ACCESS_TIME, lastAccessTime);
|
|
||||||
insertValues[i] = deviceValues;
|
insertValues[i] = deviceValues;
|
||||||
}
|
}
|
||||||
valuesBundle.putParcelableArray(BrowserContract.METHOD_PARAM_DATA, insertValues);
|
valuesBundle.putParcelableArray(BrowserContract.METHOD_PARAM_DATA, insertValues);
|
||||||
@ -108,4 +120,53 @@ public class FxAccountDeviceListUpdater implements FxAccountClient20.RequestDele
|
|||||||
final FxAccountClient fxaClient = getSynchronousFxaClient();
|
final FxAccountClient fxaClient = getSynchronousFxaClient();
|
||||||
fxaClient.deviceList(sessionToken, this);
|
fxaClient.deviceList(sessionToken, this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Updates the list of remote devices, and also renews our push registration if the list provider
|
||||||
|
// tells us it's expired.
|
||||||
|
public void updateAndMaybeRenewRegistration(final Context context) {
|
||||||
|
// Synchronous operation, the re-registration will happen right after the refresh if necessary.
|
||||||
|
this.update();
|
||||||
|
if (!this.localDevicePushEndpointExpired) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
final SharedPreferences syncPrefs;
|
||||||
|
try {
|
||||||
|
syncPrefs = fxAccount.getSyncPrefs();
|
||||||
|
} catch (UnsupportedEncodingException | GeneralSecurityException e) {
|
||||||
|
Log.e(LOG_TAG, "Could not get sync preferences, skipping push endpoint re-registration.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
final long lastTryMs = syncPrefs.getLong(SYNC_PREFS_PUSH_LAST_RENEW_REGISTRATION_MS, 0);
|
||||||
|
final long nowMs = System.currentTimeMillis();
|
||||||
|
if (nowMs - lastTryMs < TIME_BETWEEN_RENEW_REGISTRATION_MS) {
|
||||||
|
Log.w(LOG_TAG, "Last renew registration too close, skipping.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
final SharedPreferences.Editor syncPrefsEditor = syncPrefs.edit();
|
||||||
|
syncPrefsEditor.putLong(SYNC_PREFS_PUSH_LAST_RENEW_REGISTRATION_MS, nowMs);
|
||||||
|
syncPrefsEditor.commit();
|
||||||
|
|
||||||
|
ThreadUtils.postToBackgroundThread(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
try {
|
||||||
|
FxAccountDeviceListUpdater.this.renewPushRegistration(context);
|
||||||
|
} catch (ClassNotFoundException | NoSuchMethodException | InvocationTargetException | IllegalAccessException e) {
|
||||||
|
Log.e(LOG_TAG, "Could not renew push registration, continuing anyway", e);
|
||||||
|
}
|
||||||
|
FxAccountDeviceRegistrator.renewRegistration(context);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void renewPushRegistration(Context context) throws ClassNotFoundException, NoSuchMethodException,
|
||||||
|
InvocationTargetException, IllegalAccessException {
|
||||||
|
final Class<?> pushService = Class.forName("org.mozilla.gecko.push.PushService");
|
||||||
|
final Method getInstance = pushService.getMethod("getInstance", Context.class);
|
||||||
|
final Object instance = getInstance.invoke(null, context);
|
||||||
|
final Method onRefresh = pushService.getMethod("onRefresh");
|
||||||
|
onRefresh.invoke(instance);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -380,7 +380,7 @@ public class FxAccountDeviceRegistrator implements BundleEventListener {
|
|||||||
final FxAccountDevice updatedDevice = new FxAccountDevice(device.name, fxaDevice.id, device.type,
|
final FxAccountDevice updatedDevice = new FxAccountDevice(device.name, fxaDevice.id, device.type,
|
||||||
null, null,
|
null, null,
|
||||||
device.pushCallback, device.pushPublicKey,
|
device.pushCallback, device.pushPublicKey,
|
||||||
device.pushAuthKey);
|
device.pushAuthKey, null);
|
||||||
doFxaRegistration(context, fxAccount, updatedDevice, false);
|
doFxaRegistration(context, fxAccount, updatedDevice, false);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -484,7 +484,7 @@ public class FxAccountSyncAdapter extends AbstractThreadedSyncAdapter {
|
|||||||
|
|
||||||
FxAccountDeviceListUpdater deviceListUpdater = new FxAccountDeviceListUpdater(fxAccount, context.getContentResolver());
|
FxAccountDeviceListUpdater deviceListUpdater = new FxAccountDeviceListUpdater(fxAccount, context.getContentResolver());
|
||||||
// Since the clients stage requires a fresh list of remote devices, we update the device list synchronously.
|
// Since the clients stage requires a fresh list of remote devices, we update the device list synchronously.
|
||||||
deviceListUpdater.update();
|
deviceListUpdater.updateAndMaybeRenewRegistration(context);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -81,9 +81,9 @@ public class TestFxAccountDeviceListUpdater {
|
|||||||
public void testSuccessHandler() throws Throwable {
|
public void testSuccessHandler() throws Throwable {
|
||||||
FxAccountDevice[] result = new FxAccountDevice[2];
|
FxAccountDevice[] result = new FxAccountDevice[2];
|
||||||
FxAccountDevice device1 = new FxAccountDevice("Current device", "deviceid1", "mobile", true, System.currentTimeMillis(),
|
FxAccountDevice device1 = new FxAccountDevice("Current device", "deviceid1", "mobile", true, System.currentTimeMillis(),
|
||||||
"https://localhost/push/callback1", "abc123", "321cba");
|
"https://localhost/push/callback1", "abc123", "321cba", false);
|
||||||
FxAccountDevice device2 = new FxAccountDevice("Desktop PC", "deviceid2", "desktop", true, System.currentTimeMillis(),
|
FxAccountDevice device2 = new FxAccountDevice("Desktop PC", "deviceid2", "desktop", true, System.currentTimeMillis(),
|
||||||
"https://localhost/push/callback2", "abc123", "321cba");
|
"https://localhost/push/callback2", "abc123", "321cba", false);
|
||||||
result[0] = device1;
|
result[0] = device1;
|
||||||
result[1] = device2;
|
result[1] = device2;
|
||||||
|
|
||||||
|
@ -180,7 +180,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, null);
|
FxAccountDevice device = new FxAccountDevice(deviceToRegister.name, deviceId, deviceToRegister.type, null, null, null, null, null, false);
|
||||||
requestDelegate.handleSuccess(device);
|
requestDelegate.handleSuccess(device);
|
||||||
} else { // Update
|
} else { // Update
|
||||||
FxAccountDevice existingDevice = user.devices.get(deviceId);
|
FxAccountDevice existingDevice = user.devices.get(deviceId);
|
||||||
@ -190,7 +190,7 @@ public class MockFxAccountClient implements FxAccountClient {
|
|||||||
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, existingDevice.isCurrentDevice,
|
FxAccountDevice device = new FxAccountDevice(deviceName, existingDevice.id, existingDevice.type, existingDevice.isCurrentDevice,
|
||||||
existingDevice.lastAccessTime, existingDevice.pushCallback, existingDevice.pushPublicKey,existingDevice.pushAuthKey);
|
existingDevice.lastAccessTime, existingDevice.pushCallback, existingDevice.pushPublicKey,existingDevice.pushAuthKey, false);
|
||||||
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");
|
||||||
|
Loading…
Reference in New Issue
Block a user