Bug 1372655 - Notify other clients when uploading the local clients record for the first time. r=Grisha

MozReview-Commit-ID: HepBI6cbV3J

--HG--
extra : rebase_source : 621fd761f51697605c16d16a38d2a77dd98ab6a6
This commit is contained in:
Edouard Oger 2017-08-03 16:10:40 -04:00
Родитель f623d60510
Коммит 5beff3890e
4 изменённых файлов: 58 добавлений и 37 удалений

Просмотреть файл

@ -11,8 +11,6 @@ import org.mozilla.gecko.background.fxa.FxAccountClient20.TwoKeys;
import org.mozilla.gecko.fxa.devices.FxAccountDevice; import org.mozilla.gecko.fxa.devices.FxAccountDevice;
import org.mozilla.gecko.sync.ExtendedJSONObject; import org.mozilla.gecko.sync.ExtendedJSONObject;
import java.util.List;
public interface FxAccountClient { public interface FxAccountClient {
public void accountStatus(String uid, RequestDelegate<AccountStatusResponse> requestDelegate); public void accountStatus(String uid, RequestDelegate<AccountStatusResponse> requestDelegate);
public void recoveryEmailStatus(byte[] sessionToken, RequestDelegate<RecoveryEmailStatusResponse> requestDelegate); public void recoveryEmailStatus(byte[] sessionToken, RequestDelegate<RecoveryEmailStatusResponse> requestDelegate);
@ -21,5 +19,5 @@ public interface FxAccountClient {
public void registerOrUpdateDevice(byte[] sessionToken, FxAccountDevice device, RequestDelegate<FxAccountDevice> requestDelegate); public void registerOrUpdateDevice(byte[] sessionToken, FxAccountDevice device, RequestDelegate<FxAccountDevice> requestDelegate);
public void destroyDevice(byte[] sessionToken, String deviceId, RequestDelegate<ExtendedJSONObject> requestDelegate); public void destroyDevice(byte[] sessionToken, String deviceId, RequestDelegate<ExtendedJSONObject> requestDelegate);
public void deviceList(byte[] sessionToken, RequestDelegate<FxAccountDevice[]> requestDelegate); public void deviceList(byte[] sessionToken, RequestDelegate<FxAccountDevice[]> requestDelegate);
public void notifyDevices(byte[] sessionToken, List<String> deviceIds, ExtendedJSONObject payload, Long TTL, RequestDelegate<ExtendedJSONObject> requestDelegate); public void notifyDevices(byte[] sessionToken, ExtendedJSONObject body, RequestDelegate<ExtendedJSONObject> delegate);
} }

Просмотреть файл

@ -35,7 +35,6 @@ import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException; import java.security.NoSuchAlgorithmException;
import java.util.Arrays; import java.util.Arrays;
import java.util.HashMap; import java.util.HashMap;
import java.util.List;
import java.util.Locale; import java.util.Locale;
import java.util.Map; import java.util.Map;
import java.util.Map.Entry; import java.util.Map.Entry;
@ -898,7 +897,7 @@ public class FxAccountClient20 implements FxAccountClient {
} }
@Override @Override
public void notifyDevices(@NonNull byte[] sessionToken, @NonNull List<String> deviceIds, ExtendedJSONObject payload, Long TTL, RequestDelegate<ExtendedJSONObject> delegate) { public void notifyDevices(@NonNull byte[] sessionToken, ExtendedJSONObject body, RequestDelegate<ExtendedJSONObject> delegate) {
final byte[] tokenId = new byte[32]; final byte[] tokenId = new byte[32];
final byte[] reqHMACKey = new byte[32]; final byte[] reqHMACKey = new byte[32];
final byte[] requestKey = new byte[32]; final byte[] requestKey = new byte[32];
@ -910,7 +909,6 @@ public class FxAccountClient20 implements FxAccountClient {
} }
final BaseResource resource; final BaseResource resource;
final ExtendedJSONObject body = createNotifyDevicesBody(deviceIds, payload, TTL);
try { try {
resource = getBaseResource("account/devices/notify"); resource = getBaseResource("account/devices/notify");
} catch (URISyntaxException | UnsupportedEncodingException e) { } catch (URISyntaxException | UnsupportedEncodingException e) {
@ -931,20 +929,4 @@ public class FxAccountClient20 implements FxAccountClient {
post(resource, body); post(resource, body);
} }
@NonNull
@SuppressWarnings("unchecked")
private ExtendedJSONObject createNotifyDevicesBody(@NonNull List<String> deviceIds, ExtendedJSONObject payload, Long TTL) {
final ExtendedJSONObject body = new ExtendedJSONObject();
final JSONArray to = new JSONArray();
to.addAll(deviceIds);
body.put("to", to);
if (payload != null) {
body.put("payload", payload);
}
if (TTL != null) {
body.put("TTL", TTL);
}
return body;
}
} }

Просмотреть файл

@ -128,6 +128,9 @@ public class SyncClientsEngineStage extends AbstractSessionManagingSyncStage {
@Override @Override
public void handleRequestSuccess(SyncStorageResponse response) { public void handleRequestSuccess(SyncStorageResponse response) {
final Context context = session.getContext();
final Account account = FirefoxAccounts.getFirefoxAccount(context);
final AndroidFxAccount fxAccount = new AndroidFxAccount(context, account);
// Hang onto the server's last modified timestamp to use // Hang onto the server's last modified timestamp to use
// in X-If-Unmodified-Since for upload. // in X-If-Unmodified-Since for upload.
@ -139,9 +142,11 @@ public class SyncClientsEngineStage extends AbstractSessionManagingSyncStage {
// If we successfully downloaded all records but ours was not one of them // If we successfully downloaded all records but ours was not one of them
// then reset the timestamp. // then reset the timestamp.
boolean isFirstLocalClientRecordUpload = false;
if (!localAccountGUIDDownloaded) { if (!localAccountGUIDDownloaded) {
Logger.info(LOG_TAG, "Local client GUID does not exist on the server. Upload timestamp will be reset."); Logger.info(LOG_TAG, "Local client GUID does not exist on the server. Upload timestamp will be reset.");
session.config.persistServerClientRecordTimestamp(0); session.config.persistServerClientRecordTimestamp(0);
isFirstLocalClientRecordUpload = true;
} }
localAccountGUIDDownloaded = false; localAccountGUIDDownloaded = false;
@ -176,25 +181,31 @@ public class SyncClientsEngineStage extends AbstractSessionManagingSyncStage {
// before we actually uploaded the records // before we actually uploaded the records
uploadRemoteRecords(); uploadRemoteRecords();
// Notify the clients who got their record written // We will send a push notification later anyway.
notifyClients(devicesToNotify); if (!isFirstLocalClientRecordUpload) {
// Notify the clients who got their record written
notifyClients(fxAccount, devicesToNotify);
}
return; return;
} }
checkAndUpload(); checkAndUpload();
if (isFirstLocalClientRecordUpload) {
notifyAllClients(fxAccount);
}
} }
private void notifyClients(final List<String> devicesToNotify) { private void notifyClients(@NonNull AndroidFxAccount fxAccount, @NonNull List<String> devicesToNotify) {
final ExecutorService executor = Executors.newSingleThreadExecutor(); final ExtendedJSONObject body = createNotifyClientsBody(devicesToNotify);
final Context context = session.getContext(); notifyClientsHelper(fxAccount, body);
final Account account = FirefoxAccounts.getFirefoxAccount(context); }
if (account == null) {
Log.e(LOG_TAG, "Can't notify other clients: no account");
return;
}
final AndroidFxAccount fxAccount = new AndroidFxAccount(context, account);
final ExtendedJSONObject payload = createNotifyDevicesPayload();
private void notifyAllClients(@NonNull AndroidFxAccount fxAccount) {
final ExtendedJSONObject body = createNotifyAllClientsBody(fxAccount.getDeviceId());
notifyClientsHelper(fxAccount, body);
}
private void notifyClientsHelper(@NonNull AndroidFxAccount fxAccount, @NonNull ExtendedJSONObject body) {
final byte[] sessionToken; final byte[] sessionToken;
try { try {
sessionToken = fxAccount.getState().getSessionToken(); sessionToken = fxAccount.getState().getSessionToken();
@ -205,9 +216,10 @@ public class SyncClientsEngineStage extends AbstractSessionManagingSyncStage {
return; return;
} }
final ExecutorService executor = Executors.newSingleThreadExecutor();
// API doc : https://github.com/mozilla/fxa-auth-server/blob/master/docs/api.md#post-v1accountdevicesnotify // API doc : https://github.com/mozilla/fxa-auth-server/blob/master/docs/api.md#post-v1accountdevicesnotify
final FxAccountClient fxAccountClient = new FxAccountClient20(fxAccount.getAccountServerURI(), executor); final FxAccountClient fxAccountClient = new FxAccountClient20(fxAccount.getAccountServerURI(), executor);
fxAccountClient.notifyDevices(sessionToken, devicesToNotify, payload, NOTIFY_TAB_SENT_TTL_SECS, new FxAccountClient20.RequestDelegate<ExtendedJSONObject>() { fxAccountClient.notifyDevices(sessionToken, body, new FxAccountClient20.RequestDelegate<ExtendedJSONObject>() {
@Override @Override
public void handleError(Exception e) { public void handleError(Exception e) {
Log.e(LOG_TAG, "Error while notifying devices", e); Log.e(LOG_TAG, "Error while notifying devices", e);
@ -220,11 +232,39 @@ public class SyncClientsEngineStage extends AbstractSessionManagingSyncStage {
@Override @Override
public void handleSuccess(ExtendedJSONObject result) { public void handleSuccess(ExtendedJSONObject result) {
Log.i(LOG_TAG, devicesToNotify.size() + " devices notified"); Log.i(LOG_TAG, "Devices notified");
} }
}); });
} }
@NonNull
@SuppressWarnings("unchecked")
private ExtendedJSONObject createNotifyClientsBody(@NonNull List<String> devicesToNotify) {
final ExtendedJSONObject body = new ExtendedJSONObject();
final JSONArray to = new JSONArray();
to.addAll(devicesToNotify);
body.put("to", to);
createNotifyClientsHelper(body);
return body;
}
@NonNull
@SuppressWarnings("unchecked")
private ExtendedJSONObject createNotifyAllClientsBody(@NonNull String localFxADeviceId) {
final ExtendedJSONObject body = new ExtendedJSONObject();
body.put("to", "all");
final JSONArray excluded = new JSONArray();
excluded.add(localFxADeviceId);
body.put("excluded", excluded);
createNotifyClientsHelper(body);
return body;
}
private void createNotifyClientsHelper(ExtendedJSONObject body) {
body.put("payload", createNotifyDevicesPayload());
body.put("TTL", NOTIFY_TAB_SENT_TTL_SECS);
}
@NonNull @NonNull
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
private ExtendedJSONObject createNotifyDevicesPayload() { private ExtendedJSONObject createNotifyDevicesPayload() {

Просмотреть файл

@ -6,6 +6,7 @@ package org.mozilla.gecko.fxa.login;
import android.text.TextUtils; import android.text.TextUtils;
import org.mozilla.gecko.background.fxa.FxAccountClient; 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.AccountStatusResponse;
import org.mozilla.gecko.background.fxa.FxAccountClient20.RequestDelegate; import org.mozilla.gecko.background.fxa.FxAccountClient20.RequestDelegate;
import org.mozilla.gecko.background.fxa.FxAccountClient20.RecoveryEmailStatusResponse; import org.mozilla.gecko.background.fxa.FxAccountClient20.RecoveryEmailStatusResponse;
@ -239,7 +240,7 @@ public class MockFxAccountClient implements FxAccountClient {
} }
@Override @Override
public void notifyDevices(byte[] sessionToken, List<String> deviceIds, ExtendedJSONObject payload, Long TTL, RequestDelegate<ExtendedJSONObject> requestDelegate) { public void notifyDevices(byte[] sessionToken, ExtendedJSONObject body, RequestDelegate<ExtendedJSONObject> requestDelegate) {
requestDelegate.handleSuccess(new ExtendedJSONObject()); requestDelegate.handleSuccess(new ExtendedJSONObject());
} }
} }