diff --git a/pom.xml b/pom.xml
index 14abbc82e..4155d63e6 100644
--- a/pom.xml
+++ b/pom.xml
@@ -129,7 +129,7 @@
org.whispersystems
websocket-resources
- 0.2.0
+ 0.2.1
diff --git a/src/main/java/org/whispersystems/textsecuregcm/controllers/AccountController.java b/src/main/java/org/whispersystems/textsecuregcm/controllers/AccountController.java
index 5e9476628..7ca3b6240 100644
--- a/src/main/java/org/whispersystems/textsecuregcm/controllers/AccountController.java
+++ b/src/main/java/org/whispersystems/textsecuregcm/controllers/AccountController.java
@@ -197,10 +197,14 @@ public class AccountController {
@PUT
@Path("/gcm/")
@Consumes(MediaType.APPLICATION_JSON)
- public void setGcmRegistrationId(@Auth Account account, @Valid GcmRegistrationId registrationId) {
+ public void setGcmRegistrationId(@Auth Account account, @Valid GcmRegistrationId registrationId) {
Device device = account.getAuthenticatedDevice().get();
device.setApnId(null);
device.setGcmId(registrationId.getGcmRegistrationId());
+
+ if (registrationId.isWebSocketChannel()) device.setFetchesMessages(true);
+ else device.setFetchesMessages(false);
+
accounts.update(account);
}
@@ -210,6 +214,7 @@ public class AccountController {
public void deleteGcmRegistrationId(@Auth Account account) {
Device device = account.getAuthenticatedDevice().get();
device.setGcmId(null);
+ device.setFetchesMessages(false);
accounts.update(account);
}
@@ -221,6 +226,7 @@ public class AccountController {
Device device = account.getAuthenticatedDevice().get();
device.setApnId(registrationId.getApnRegistrationId());
device.setGcmId(null);
+ device.setFetchesMessages(true);
accounts.update(account);
}
@@ -230,6 +236,25 @@ public class AccountController {
public void deleteApnRegistrationId(@Auth Account account) {
Device device = account.getAuthenticatedDevice().get();
device.setApnId(null);
+ device.setFetchesMessages(false);
+ accounts.update(account);
+ }
+
+ @Timed
+ @PUT
+ @Path("/wsc/")
+ public void setWebSocketChannelSupported(@Auth Account account) {
+ Device device = account.getAuthenticatedDevice().get();
+ device.setFetchesMessages(true);
+ accounts.update(account);
+ }
+
+ @Timed
+ @DELETE
+ @Path("/wsc/")
+ public void deleteWebSocketChannel(@Auth Account account) {
+ Device device = account.getAuthenticatedDevice().get();
+ device.setFetchesMessages(false);
accounts.update(account);
}
diff --git a/src/main/java/org/whispersystems/textsecuregcm/entities/GcmMessage.java b/src/main/java/org/whispersystems/textsecuregcm/entities/GcmMessage.java
index 69b46a4e6..e0e8b29bb 100644
--- a/src/main/java/org/whispersystems/textsecuregcm/entities/GcmMessage.java
+++ b/src/main/java/org/whispersystems/textsecuregcm/entities/GcmMessage.java
@@ -20,20 +20,23 @@ public class GcmMessage {
private int deviceId;
@JsonProperty
- @NotEmpty
private String message;
@JsonProperty
private boolean receipt;
+ @JsonProperty
+ private boolean notification;
+
public GcmMessage() {}
- public GcmMessage(String gcmId, String number, int deviceId, String message, boolean receipt) {
- this.gcmId = gcmId;
- this.number = number;
- this.deviceId = deviceId;
- this.message = message;
- this.receipt = receipt;
+ public GcmMessage(String gcmId, String number, int deviceId, String message, boolean receipt, boolean notification) {
+ this.gcmId = gcmId;
+ this.number = number;
+ this.deviceId = deviceId;
+ this.message = message;
+ this.receipt = receipt;
+ this.notification = notification;
}
}
diff --git a/src/main/java/org/whispersystems/textsecuregcm/entities/GcmRegistrationId.java b/src/main/java/org/whispersystems/textsecuregcm/entities/GcmRegistrationId.java
index dc1dfec01..d7b4e2f65 100644
--- a/src/main/java/org/whispersystems/textsecuregcm/entities/GcmRegistrationId.java
+++ b/src/main/java/org/whispersystems/textsecuregcm/entities/GcmRegistrationId.java
@@ -25,9 +25,15 @@ public class GcmRegistrationId {
@NotEmpty
private String gcmRegistrationId;
+ @JsonProperty
+ private boolean webSocketChannel;
+
public String getGcmRegistrationId() {
return gcmRegistrationId;
}
+ public boolean isWebSocketChannel() {
+ return webSocketChannel;
+ }
}
diff --git a/src/main/java/org/whispersystems/textsecuregcm/push/PushSender.java b/src/main/java/org/whispersystems/textsecuregcm/push/PushSender.java
index 64499dde8..c9e0a65a5 100644
--- a/src/main/java/org/whispersystems/textsecuregcm/push/PushSender.java
+++ b/src/main/java/org/whispersystems/textsecuregcm/push/PushSender.java
@@ -57,6 +57,13 @@ public class PushSender {
private void sendGcmMessage(Account account, Device device, OutgoingMessageSignal message)
throws TransientPushFailureException, NotPushRegisteredException
+ {
+ if (device.getFetchesMessages()) sendNotificationGcmMessage(account, device, message);
+ else sendPayloadGcmMessage(account, device, message);
+ }
+
+ private void sendPayloadGcmMessage(Account account, Device device, OutgoingMessageSignal message)
+ throws TransientPushFailureException, NotPushRegisteredException
{
try {
String number = account.getNumber();
@@ -65,7 +72,7 @@ public class PushSender {
boolean isReceipt = message.getType() == OutgoingMessageSignal.Type.RECEIPT_VALUE;
EncryptedOutgoingMessage encryptedMessage = new EncryptedOutgoingMessage(message, device.getSignalingKey());
GcmMessage gcmMessage = new GcmMessage(registrationId, number, (int) deviceId,
- encryptedMessage.toEncodedString(), isReceipt);
+ encryptedMessage.toEncodedString(), isReceipt, false);
pushServiceClient.send(gcmMessage);
} catch (CryptoEncodingException e) {
@@ -73,10 +80,26 @@ public class PushSender {
}
}
+ private void sendNotificationGcmMessage(Account account, Device device, OutgoingMessageSignal message)
+ throws TransientPushFailureException
+ {
+ DeliveryStatus deliveryStatus = webSocketSender.sendMessage(account, device, message, WebsocketSender.Type.GCM);
+
+ if (!deliveryStatus.isDelivered()) {
+ GcmMessage gcmMessage = new GcmMessage(device.getGcmId(), account.getNumber(),
+ (int)device.getId(), "", false, true);
+
+ pushServiceClient.send(gcmMessage);
+ } else {
+ logger.warn("Delivered!");
+ }
+
+ }
+
private void sendApnMessage(Account account, Device device, OutgoingMessageSignal outgoingMessage)
throws TransientPushFailureException
{
- DeliveryStatus deliveryStatus = webSocketSender.sendMessage(account, device, outgoingMessage, true);
+ DeliveryStatus deliveryStatus = webSocketSender.sendMessage(account, device, outgoingMessage, WebsocketSender.Type.APN);
if (!deliveryStatus.isDelivered() && outgoingMessage.getType() != OutgoingMessageSignal.Type.RECEIPT_VALUE) {
ApnMessage apnMessage = new ApnMessage(device.getApnId(), account.getNumber(), (int)device.getId(),
@@ -87,6 +110,6 @@ public class PushSender {
private void sendWebSocketMessage(Account account, Device device, OutgoingMessageSignal outgoingMessage)
{
- webSocketSender.sendMessage(account, device, outgoingMessage, false);
+ webSocketSender.sendMessage(account, device, outgoingMessage, WebsocketSender.Type.WEB);
}
}
diff --git a/src/main/java/org/whispersystems/textsecuregcm/push/WebsocketSender.java b/src/main/java/org/whispersystems/textsecuregcm/push/WebsocketSender.java
index d3adece66..942073bab 100644
--- a/src/main/java/org/whispersystems/textsecuregcm/push/WebsocketSender.java
+++ b/src/main/java/org/whispersystems/textsecuregcm/push/WebsocketSender.java
@@ -36,6 +36,12 @@ import static org.whispersystems.textsecuregcm.storage.PubSubProtos.PubSubMessag
public class WebsocketSender {
+ public static enum Type {
+ APN,
+ GCM,
+ WEB
+ }
+
private static final Logger logger = LoggerFactory.getLogger(WebsocketSender.class);
private final MetricRegistry metricRegistry = SharedMetricRegistries.getOrCreate(Constants.METRICS_NAME);
@@ -46,6 +52,9 @@ public class WebsocketSender {
private final Meter apnOnlineMeter = metricRegistry.meter(name(getClass(), "apn_online" ));
private final Meter apnOfflineMeter = metricRegistry.meter(name(getClass(), "apn_offline"));
+ private final Meter gcmOnlineMeter = metricRegistry.meter(name(getClass(), "gcm_online" ));
+ private final Meter gcmOfflineMeter = metricRegistry.meter(name(getClass(), "gcm_offline"));
+
private final Meter provisioningOnlineMeter = metricRegistry.meter(name(getClass(), "provisioning_online" ));
private final Meter provisioningOfflineMeter = metricRegistry.meter(name(getClass(), "provisioning_offline"));
@@ -57,7 +66,7 @@ public class WebsocketSender {
this.pubSubManager = pubSubManager;
}
- public DeliveryStatus sendMessage(Account account, Device device, OutgoingMessageSignal message, boolean apn) {
+ public DeliveryStatus sendMessage(Account account, Device device, OutgoingMessageSignal message, Type channel) {
WebsocketAddress address = new WebsocketAddress(account.getNumber(), device.getId());
PubSubMessage pubSubMessage = PubSubMessage.newBuilder()
.setType(PubSubMessage.Type.DELIVER)
@@ -65,13 +74,15 @@ public class WebsocketSender {
.build();
if (pubSubManager.publish(address, pubSubMessage)) {
- if (apn) apnOnlineMeter.mark();
- else websocketOnlineMeter.mark();
+ if (channel == Type.APN) apnOnlineMeter.mark();
+ else if (channel == Type.GCM) gcmOnlineMeter.mark();
+ else websocketOnlineMeter.mark();
return new DeliveryStatus(true, 0);
} else {
- if (apn) apnOfflineMeter.mark();
- else websocketOfflineMeter.mark();
+ if (channel == Type.APN) apnOfflineMeter.mark();
+ else if (channel == Type.GCM) gcmOfflineMeter.mark();
+ else websocketOfflineMeter.mark();
int queueDepth = messagesManager.insert(account.getNumber(), device.getId(), message);
pubSubManager.publish(address, PubSubMessage.newBuilder()
diff --git a/src/main/java/org/whispersystems/textsecuregcm/websocket/AuthenticatedConnectListener.java b/src/main/java/org/whispersystems/textsecuregcm/websocket/AuthenticatedConnectListener.java
index 0d443ebd0..461e4c0d1 100644
--- a/src/main/java/org/whispersystems/textsecuregcm/websocket/AuthenticatedConnectListener.java
+++ b/src/main/java/org/whispersystems/textsecuregcm/websocket/AuthenticatedConnectListener.java
@@ -33,30 +33,17 @@ public class AuthenticatedConnectListener implements WebSocketConnectListener {
@Override
public void onWebSocketConnect(WebSocketSessionContext context) {
- Optional account = context.getAuthenticated(Account.class);
+ Account account = context.getAuthenticated(Account.class).get();
+ Device device = account.getAuthenticatedDevice().get();
- if (!account.isPresent()) {
- logger.debug("WS Connection with no authentication...");
- context.getClient().close(4001, "Authentication failed");
- return;
- }
-
- Optional device = account.get().getAuthenticatedDevice();
-
- if (!device.isPresent()) {
- logger.debug("WS Connection with no authenticated device...");
- context.getClient().close(4001, "Device authentication failed");
- return;
- }
-
- if (device.get().getLastSeen() != Util.todayInMillis()) {
- device.get().setLastSeen(Util.todayInMillis());
- accountsManager.update(account.get());
+ if (device.getLastSeen() != Util.todayInMillis()) {
+ device.setLastSeen(Util.todayInMillis());
+ accountsManager.update(account);
}
final WebSocketConnection connection = new WebSocketConnection(accountsManager, pushSender,
messagesManager, pubSubManager,
- account.get(), device.get(),
+ account, device,
context.getClient());
connection.onConnected();
diff --git a/src/main/java/org/whispersystems/textsecuregcm/websocket/WebSocketAccountAuthenticator.java b/src/main/java/org/whispersystems/textsecuregcm/websocket/WebSocketAccountAuthenticator.java
index df2d04618..004bd18e4 100644
--- a/src/main/java/org/whispersystems/textsecuregcm/websocket/WebSocketAccountAuthenticator.java
+++ b/src/main/java/org/whispersystems/textsecuregcm/websocket/WebSocketAccountAuthenticator.java
@@ -34,7 +34,9 @@ public class WebSocketAccountAuthenticator implements WebSocketAuthenticator