Track voice support on TS server.

// FREEBIE
This commit is contained in:
Moxie Marlinspike 2015-08-13 11:25:05 -07:00
parent 4c3aae63d3
commit 62d8f635b0
24 changed files with 108 additions and 64 deletions

View File

@ -9,7 +9,7 @@
<groupId>org.whispersystems.textsecure</groupId> <groupId>org.whispersystems.textsecure</groupId>
<artifactId>TextSecureServer</artifactId> <artifactId>TextSecureServer</artifactId>
<version>0.71</version> <version>0.75</version>
<properties> <properties>
<dropwizard.version>0.9.0-rc3</dropwizard.version> <dropwizard.version>0.9.0-rc3</dropwizard.version>

View File

@ -18,6 +18,8 @@ package org.whispersystems.textsecuregcm;
import com.codahale.metrics.SharedMetricRegistries; import com.codahale.metrics.SharedMetricRegistries;
import com.codahale.metrics.graphite.GraphiteReporter; import com.codahale.metrics.graphite.GraphiteReporter;
import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.DeserializationFeature; import com.fasterxml.jackson.databind.DeserializationFeature;
import com.google.common.base.Optional; import com.google.common.base.Optional;
import org.bouncycastle.jce.provider.BouncyCastleProvider; import org.bouncycastle.jce.provider.BouncyCastleProvider;
@ -148,6 +150,8 @@ public class WhisperServerService extends Application<WhisperServerConfiguration
{ {
SharedMetricRegistries.add(Constants.METRICS_NAME, environment.metrics()); SharedMetricRegistries.add(Constants.METRICS_NAME, environment.metrics());
environment.getObjectMapper().configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); environment.getObjectMapper().configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
environment.getObjectMapper().setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.NONE);
environment.getObjectMapper().setVisibility(PropertyAccessor.FIELD, JsonAutoDetect.Visibility.ANY);
DBIFactory dbiFactory = new DBIFactory(); DBIFactory dbiFactory = new DBIFactory();
DBI database = dbiFactory.build(environment, config.getDataSourceFactory(), "accountdb"); DBI database = dbiFactory.build(environment, config.getDataSourceFactory(), "accountdb");

View File

@ -250,19 +250,17 @@ public class AccountController {
@Timed @Timed
@PUT @PUT
@Path("/wsc/") @Path("/attributes/")
public void setWebSocketChannelSupported(@Auth Account account) { public void setAccountAttributes(@Auth Account account, @Valid AccountAttributes attributes) {
Device device = account.getAuthenticatedDevice().get(); Device device = account.getAuthenticatedDevice().get();
device.setFetchesMessages(true);
accounts.update(account);
}
@Timed device.setFetchesMessages(attributes.getFetchesMessages());
@DELETE device.setName(attributes.getName());
@Path("/wsc/") device.setLastSeen(Util.todayInMillis());
public void deleteWebSocketChannel(@Auth Account account) { device.setVoiceSupported(attributes.getVoice());
Device device = account.getAuthenticatedDevice().get(); device.setRegistrationId(attributes.getRegistrationId());
device.setFetchesMessages(false); device.setSignalingKey(attributes.getSignalingKey());
accounts.update(account); accounts.update(account);
} }
@ -283,6 +281,7 @@ public class AccountController {
device.setFetchesMessages(accountAttributes.getFetchesMessages()); device.setFetchesMessages(accountAttributes.getFetchesMessages());
device.setRegistrationId(accountAttributes.getRegistrationId()); device.setRegistrationId(accountAttributes.getRegistrationId());
device.setName(accountAttributes.getName()); device.setName(accountAttributes.getName());
device.setVoiceSupported(accountAttributes.getVoice());
device.setCreated(System.currentTimeMillis()); device.setCreated(System.currentTimeMillis());
device.setLastSeen(Util.todayInMillis()); device.setLastSeen(Util.todayInMillis());
@ -293,8 +292,6 @@ public class AccountController {
accounts.create(account); accounts.create(account);
messagesManager.clear(number); messagesManager.clear(number);
pendingAccounts.remove(number); pendingAccounts.remove(number);
logger.debug("Stored device...");
} }
@VisibleForTesting protected VerificationCode generateVerificationCode(String number) { @VisibleForTesting protected VerificationCode generateVerificationCode(String number) {

View File

@ -150,7 +150,7 @@ public class FederationControllerV1 extends FederationController {
for (Account account : accountList) { for (Account account : accountList) {
byte[] token = Util.getContactToken(account.getNumber()); byte[] token = Util.getContactToken(account.getNumber());
ClientContact clientContact = new ClientContact(token, null); ClientContact clientContact = new ClientContact(token, null, account.isVoiceSupported());
if (!account.isActive()) { if (!account.isActive()) {
clientContact.setInactive(true); clientContact.setInactive(true);

View File

@ -21,8 +21,6 @@ import com.google.common.annotations.VisibleForTesting;
import org.hibernate.validator.constraints.Length; import org.hibernate.validator.constraints.Length;
import org.hibernate.validator.constraints.NotEmpty; import org.hibernate.validator.constraints.NotEmpty;
import javax.validation.constraints.Max;
public class AccountAttributes { public class AccountAttributes {
@JsonProperty @JsonProperty
@ -39,19 +37,23 @@ public class AccountAttributes {
@Length(max = 50, message = "This field must be less than 50 characters") @Length(max = 50, message = "This field must be less than 50 characters")
private String name; private String name;
@JsonProperty
private boolean voice;
public AccountAttributes() {} public AccountAttributes() {}
@VisibleForTesting @VisibleForTesting
public AccountAttributes(String signalingKey, boolean fetchesMessages, int registrationId) { public AccountAttributes(String signalingKey, boolean fetchesMessages, int registrationId) {
this(signalingKey, fetchesMessages, registrationId, null); this(signalingKey, fetchesMessages, registrationId, null, false);
} }
@VisibleForTesting @VisibleForTesting
public AccountAttributes(String signalingKey, boolean fetchesMessages, int registrationId, String name) { public AccountAttributes(String signalingKey, boolean fetchesMessages, int registrationId, String name, boolean voice) {
this.signalingKey = signalingKey; this.signalingKey = signalingKey;
this.fetchesMessages = fetchesMessages; this.fetchesMessages = fetchesMessages;
this.registrationId = registrationId; this.registrationId = registrationId;
this.name = name; this.name = name;
this.voice = voice;
} }
public String getSignalingKey() { public String getSignalingKey() {
@ -69,4 +71,8 @@ public class AccountAttributes {
public String getName() { public String getName() {
return name; return name;
} }
public boolean getVoice() {
return voice;
}
} }

View File

@ -32,12 +32,16 @@ public class ClientContact {
@JsonProperty @JsonProperty
private byte[] token; private byte[] token;
@JsonProperty
private boolean voice;
private String relay; private String relay;
private boolean inactive; private boolean inactive;
public ClientContact(byte[] token, String relay) { public ClientContact(byte[] token, String relay, boolean voice) {
this.token = token; this.token = token;
this.relay = relay; this.relay = relay;
this.voice = voice;
} }
public ClientContact() {} public ClientContact() {}
@ -62,9 +66,13 @@ public class ClientContact {
this.inactive = inactive; this.inactive = inactive;
} }
// public String toString() { public boolean isVoice() {
// return new Gson().toJson(this); return voice;
// } }
public void setVoice(boolean voice) {
this.voice = voice;
}
@Override @Override
public boolean equals(Object other) { public boolean equals(Object other) {
@ -76,6 +84,7 @@ public class ClientContact {
return return
Arrays.equals(this.token, that.token) && Arrays.equals(this.token, that.token) &&
this.inactive == that.inactive && this.inactive == that.inactive &&
this.voice == that.voice &&
(this.relay == null ? (that.relay == null) : this.relay.equals(that.relay)); (this.relay == null ? (that.relay == null) : this.relay.equals(that.relay));
} }

View File

@ -40,6 +40,6 @@ public class NonLimitedAccount extends Account {
@Override @Override
public Optional<Device> getAuthenticatedDevice() { public Optional<Device> getAuthenticatedDevice() {
return Optional.of(new Device(deviceId, null, null, null, null, null, null, null, false, 0, null, System.currentTimeMillis(), System.currentTimeMillis())); return Optional.of(new Device(deviceId, null, null, null, null, null, null, null, false, 0, null, System.currentTimeMillis(), System.currentTimeMillis(), false));
} }
} }

View File

@ -31,9 +31,7 @@ public class RedisHealthCheck extends HealthCheck {
@Override @Override
protected Result check() throws Exception { protected Result check() throws Exception {
Jedis client = clientPool.getResource(); try (Jedis client = clientPool.getResource()) {
try {
client.set("HEALTH", "test"); client.set("HEALTH", "test");
if (!"test".equals(client.get("HEALTH"))) { if (!"test".equals(client.get("HEALTH"))) {
@ -41,8 +39,6 @@ public class RedisHealthCheck extends HealthCheck {
} }
return Result.healthy(); return Result.healthy();
} finally {
clientPool.returnResource(client);
} }
} }
} }

View File

@ -82,6 +82,7 @@ public class FeedbackHandler implements Managed, Runnable {
device.get().setGcmId(event.getCanonicalId()); device.get().setGcmId(event.getCanonicalId());
} else { } else {
device.get().setGcmId(null); device.get().setGcmId(null);
device.get().setFetchesMessages(false);
} }
accountsManager.update(account.get()); accountsManager.update(account.get());
} }

View File

@ -71,7 +71,7 @@ public class Account {
} }
public void removeDevice(long deviceId) { public void removeDevice(long deviceId) {
this.devices.remove(new Device(deviceId, null, null, null, null, null, null, null, false, 0, null, 0, 0)); this.devices.remove(new Device(deviceId, null, null, null, null, null, null, null, false, 0, null, 0, 0, false));
} }
public Set<Device> getDevices() { public Set<Device> getDevices() {
@ -92,6 +92,16 @@ public class Account {
return Optional.absent(); return Optional.absent();
} }
public boolean isVoiceSupported() {
for (Device device : devices) {
if (device.isActive() && device.isVoiceSupported()) {
return true;
}
}
return false;
}
public boolean isActive() { public boolean isActive() {
return return
getMasterDevice().isPresent() && getMasterDevice().isPresent() &&

View File

@ -16,8 +16,6 @@
*/ */
package org.whispersystems.textsecuregcm.storage; package org.whispersystems.textsecuregcm.storage;
import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.ObjectMapper;
import org.skife.jdbi.v2.SQLStatement; import org.skife.jdbi.v2.SQLStatement;

View File

@ -100,7 +100,7 @@ public class AccountsManager {
private void updateDirectory(Account account) { private void updateDirectory(Account account) {
if (account.isActive()) { if (account.isActive()) {
byte[] token = Util.getContactToken(account.getNumber()); byte[] token = Util.getContactToken(account.getNumber());
ClientContact clientContact = new ClientContact(token, null); ClientContact clientContact = new ClientContact(token, null, account.isVoiceSupported());
directory.add(clientContact); directory.add(clientContact);
} else { } else {
directory.remove(account.getNumber()); directory.remove(account.getNumber());

View File

@ -70,13 +70,16 @@ public class Device {
@JsonProperty @JsonProperty
private long created; private long created;
@JsonProperty
private boolean voice;
public Device() {} public Device() {}
public Device(long id, String name, String authToken, String salt, public Device(long id, String name, String authToken, String salt,
String signalingKey, String gcmId, String apnId, String signalingKey, String gcmId, String apnId,
String voipApnId, boolean fetchesMessages, String voipApnId, boolean fetchesMessages,
int registrationId, SignedPreKey signedPreKey, int registrationId, SignedPreKey signedPreKey,
long lastSeen, long created) long lastSeen, long created, boolean voice)
{ {
this.id = id; this.id = id;
this.name = name; this.name = name;
@ -91,6 +94,7 @@ public class Device {
this.signedPreKey = signedPreKey; this.signedPreKey = signedPreKey;
this.lastSeen = lastSeen; this.lastSeen = lastSeen;
this.created = created; this.created = created;
this.voice = voice;
} }
public String getApnId() { public String getApnId() {
@ -157,6 +161,14 @@ public class Device {
this.name = name; this.name = name;
} }
public boolean isVoiceSupported() {
return voice;
}
public void setVoiceSupported(boolean voice) {
this.voice = voice;
}
public void setAuthenticationCredentials(AuthenticationCredentials credentials) { public void setAuthenticationCredentials(AuthenticationCredentials credentials) {
this.authToken = credentials.getHashedAuthenticationToken(); this.authToken = credentials.getHashedAuthenticationToken();
this.salt = credentials.getSalt(); this.salt = credentials.getSalt();

View File

@ -61,9 +61,9 @@ public class DirectoryManager {
} }
public void remove(byte[] token) { public void remove(byte[] token) {
Jedis jedis = redisPool.getResource(); try (Jedis jedis = redisPool.getResource()) {
jedis.hdel(DIRECTORY_KEY, token); jedis.hdel(DIRECTORY_KEY, token);
redisPool.returnResource(jedis); }
} }
public void remove(BatchOperationHandle handle, byte[] token) { public void remove(BatchOperationHandle handle, byte[] token) {
@ -72,7 +72,7 @@ public class DirectoryManager {
} }
public void add(ClientContact contact) { public void add(ClientContact contact) {
TokenValue tokenValue = new TokenValue(contact.getRelay()); TokenValue tokenValue = new TokenValue(contact.getRelay(), contact.isVoice());
try (Jedis jedis = redisPool.getResource()) { try (Jedis jedis = redisPool.getResource()) {
jedis.hset(DIRECTORY_KEY, contact.getToken(), objectMapper.writeValueAsBytes(tokenValue)); jedis.hset(DIRECTORY_KEY, contact.getToken(), objectMapper.writeValueAsBytes(tokenValue));
@ -84,7 +84,7 @@ public class DirectoryManager {
public void add(BatchOperationHandle handle, ClientContact contact) { public void add(BatchOperationHandle handle, ClientContact contact) {
try { try {
Pipeline pipeline = handle.pipeline; Pipeline pipeline = handle.pipeline;
TokenValue tokenValue = new TokenValue(contact.getRelay()); TokenValue tokenValue = new TokenValue(contact.getRelay(), contact.isVoice());
pipeline.hset(DIRECTORY_KEY, contact.getToken(), objectMapper.writeValueAsBytes(tokenValue)); pipeline.hset(DIRECTORY_KEY, contact.getToken(), objectMapper.writeValueAsBytes(tokenValue));
} catch (JsonProcessingException e) { } catch (JsonProcessingException e) {
@ -106,7 +106,7 @@ public class DirectoryManager {
} }
TokenValue tokenValue = objectMapper.readValue(result, TokenValue.class); TokenValue tokenValue = objectMapper.readValue(result, TokenValue.class);
return Optional.of(new ClientContact(token, tokenValue.relay)); return Optional.of(new ClientContact(token, tokenValue.relay, tokenValue.voice));
} catch (IOException e) { } catch (IOException e) {
logger.warn("JSON Error", e); logger.warn("JSON Error", e);
return Optional.absent(); return Optional.absent();
@ -133,7 +133,7 @@ public class DirectoryManager {
try { try {
if (pair.second().get() != null) { if (pair.second().get() != null) {
TokenValue tokenValue = objectMapper.readValue(pair.second().get(), TokenValue.class); TokenValue tokenValue = objectMapper.readValue(pair.second().get(), TokenValue.class);
ClientContact clientContact = new ClientContact(pair.first(), tokenValue.relay); ClientContact clientContact = new ClientContact(pair.first(), tokenValue.relay, tokenValue.voice);
results.add(clientContact); results.add(clientContact);
} }
@ -175,10 +175,14 @@ public class DirectoryManager {
@JsonProperty(value = "r") @JsonProperty(value = "r")
private String relay; private String relay;
@JsonProperty(value = "v")
private boolean voice;
public TokenValue() {} public TokenValue() {}
public TokenValue(String relay) { public TokenValue(String relay, boolean voice) {
this.relay = relay; this.relay = relay;
this.voice = voice;
} }
} }
@ -201,7 +205,7 @@ public class DirectoryManager {
} }
TokenValue tokenValue = objectMapper.readValue(result, TokenValue.class); TokenValue tokenValue = objectMapper.readValue(result, TokenValue.class);
return Optional.of(new ClientContact(token, tokenValue.relay)); return Optional.of(new ClientContact(token, tokenValue.relay, tokenValue.voice));
} }
} }

View File

@ -26,7 +26,6 @@ import org.whispersystems.textsecuregcm.storage.Account;
import org.whispersystems.textsecuregcm.storage.AccountsManager; import org.whispersystems.textsecuregcm.storage.AccountsManager;
import org.whispersystems.textsecuregcm.storage.DirectoryManager; import org.whispersystems.textsecuregcm.storage.DirectoryManager;
import org.whispersystems.textsecuregcm.storage.DirectoryManager.BatchOperationHandle; import org.whispersystems.textsecuregcm.storage.DirectoryManager.BatchOperationHandle;
import org.whispersystems.textsecuregcm.util.Base64;
import org.whispersystems.textsecuregcm.util.Util; import org.whispersystems.textsecuregcm.util.Util;
import java.io.IOException; import java.io.IOException;
@ -73,7 +72,7 @@ public class DirectoryUpdater {
for (Account account : accounts) { for (Account account : accounts) {
if (account.isActive()) { if (account.isActive()) {
byte[] token = Util.getContactToken(account.getNumber()); byte[] token = Util.getContactToken(account.getNumber());
ClientContact clientContact = new ClientContact(token, null); ClientContact clientContact = new ClientContact(token, null, account.isVoiceSupported());
directory.add(batchOperation, clientContact); directory.add(batchOperation, clientContact);
contactsAdded++; contactsAdded++;

View File

@ -5,10 +5,7 @@ import org.skife.jdbi.v2.DBI;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.whispersystems.textsecuregcm.WhisperServerConfiguration; import org.whispersystems.textsecuregcm.WhisperServerConfiguration;
import org.whispersystems.textsecuregcm.storage.Accounts;
import org.whispersystems.textsecuregcm.storage.Keys;
import org.whispersystems.textsecuregcm.storage.Messages; import org.whispersystems.textsecuregcm.storage.Messages;
import org.whispersystems.textsecuregcm.storage.PendingAccounts;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;

View File

@ -19,6 +19,7 @@ import org.whispersystems.textsecuregcm.storage.AccountsManager;
import org.whispersystems.textsecuregcm.storage.MessagesManager; import org.whispersystems.textsecuregcm.storage.MessagesManager;
import org.whispersystems.textsecuregcm.storage.PendingAccountsManager; import org.whispersystems.textsecuregcm.storage.PendingAccountsManager;
import org.whispersystems.textsecuregcm.tests.util.AuthHelper; import org.whispersystems.textsecuregcm.tests.util.AuthHelper;
import org.whispersystems.textsecuregcm.util.SystemMapper;
import javax.ws.rs.client.Entity; import javax.ws.rs.client.Entity;
import javax.ws.rs.core.MediaType; import javax.ws.rs.core.MediaType;
@ -47,6 +48,7 @@ public class AccountControllerTest {
public final ResourceTestRule resources = ResourceTestRule.builder() public final ResourceTestRule resources = ResourceTestRule.builder()
.addProvider(AuthHelper.getAuthFilter()) .addProvider(AuthHelper.getAuthFilter())
.addProvider(new AuthValueFactoryProvider.Binder()) .addProvider(new AuthValueFactoryProvider.Binder())
.setMapper(SystemMapper.getMapper())
.setTestContainerFactory(new GrizzlyWebTestContainerFactory()) .setTestContainerFactory(new GrizzlyWebTestContainerFactory())
.addResource(new AccountController(pendingAccountsManager, .addResource(new AccountController(pendingAccountsManager,
accountsManager, accountsManager,

View File

@ -135,7 +135,7 @@ public class DeviceControllerTest {
.target("/v1/devices/5678901") .target("/v1/devices/5678901")
.request() .request()
.header("Authorization", AuthHelper.getAuthHeader(AuthHelper.VALID_NUMBER, "password1")) .header("Authorization", AuthHelper.getAuthHeader(AuthHelper.VALID_NUMBER, "password1"))
.put(Entity.entity(new AccountAttributes("keykeykeykey", false, 1234, "this is a really long name that is longer than 80 characters"), .put(Entity.entity(new AccountAttributes("keykeykeykey", false, 1234, "this is a really long name that is longer than 80 characters", true),
MediaType.APPLICATION_JSON_TYPE)); MediaType.APPLICATION_JSON_TYPE));
assertEquals(response.getStatus(), 422); assertEquals(response.getStatus(), 422);

View File

@ -79,12 +79,12 @@ public class FederatedControllerTest {
@Before @Before
public void setup() throws Exception { public void setup() throws Exception {
Set<Device> singleDeviceList = new HashSet<Device>() {{ Set<Device> singleDeviceList = new HashSet<Device>() {{
add(new Device(1, null, "foo", "bar", "baz", "isgcm", null, null, false, 111, null, System.currentTimeMillis(), System.currentTimeMillis())); add(new Device(1, null, "foo", "bar", "baz", "isgcm", null, null, false, 111, null, System.currentTimeMillis(), System.currentTimeMillis(), false));
}}; }};
Set<Device> multiDeviceList = new HashSet<Device>() {{ Set<Device> multiDeviceList = new HashSet<Device>() {{
add(new Device(1, null, "foo", "bar", "baz", "isgcm", null, null, false, 222, null, System.currentTimeMillis(), System.currentTimeMillis())); add(new Device(1, null, "foo", "bar", "baz", "isgcm", null, null, false, 222, null, System.currentTimeMillis(), System.currentTimeMillis(), false));
add(new Device(2, null, "foo", "bar", "baz", "isgcm", null, null, false, 333, null, System.currentTimeMillis(), System.currentTimeMillis())); add(new Device(2, null, "foo", "bar", "baz", "isgcm", null, null, false, 333, null, System.currentTimeMillis(), System.currentTimeMillis(), false));
}}; }};
Account singleDeviceAccount = new Account(SINGLE_DEVICE_RECIPIENT, singleDeviceList); Account singleDeviceAccount = new Account(SINGLE_DEVICE_RECIPIENT, singleDeviceList);

View File

@ -74,13 +74,13 @@ public class MessageControllerTest {
@Before @Before
public void setup() throws Exception { public void setup() throws Exception {
Set<Device> singleDeviceList = new HashSet<Device>() {{ Set<Device> singleDeviceList = new HashSet<Device>() {{
add(new Device(1, null, "foo", "bar", "baz", "isgcm", null, null, false, 111, null, System.currentTimeMillis(), System.currentTimeMillis())); add(new Device(1, null, "foo", "bar", "baz", "isgcm", null, null, false, 111, null, System.currentTimeMillis(), System.currentTimeMillis(), false));
}}; }};
Set<Device> multiDeviceList = new HashSet<Device>() {{ Set<Device> multiDeviceList = new HashSet<Device>() {{
add(new Device(1, null, "foo", "bar", "baz", "isgcm", null, null, false, 222, new SignedPreKey(111, "foo", "bar"), System.currentTimeMillis(), System.currentTimeMillis())); add(new Device(1, null, "foo", "bar", "baz", "isgcm", null, null, false, 222, new SignedPreKey(111, "foo", "bar"), System.currentTimeMillis(), System.currentTimeMillis(), false));
add(new Device(2, null, "foo", "bar", "baz", "isgcm", null, null, false, 333, new SignedPreKey(222, "oof", "rab"), System.currentTimeMillis(), System.currentTimeMillis())); add(new Device(2, null, "foo", "bar", "baz", "isgcm", null, null, false, 333, new SignedPreKey(222, "oof", "rab"), System.currentTimeMillis(), System.currentTimeMillis(), false));
add(new Device(3, null, "foo", "bar", "baz", "isgcm", null, null, false, 444, null, System.currentTimeMillis() - TimeUnit.DAYS.toMillis(31), System.currentTimeMillis())); add(new Device(3, null, "foo", "bar", "baz", "isgcm", null, null, false, 444, null, System.currentTimeMillis() - TimeUnit.DAYS.toMillis(31), System.currentTimeMillis(), false));
}}; }};
Account singleDeviceAccount = new Account(SINGLE_DEVICE_RECIPIENT, singleDeviceList); Account singleDeviceAccount = new Account(SINGLE_DEVICE_RECIPIENT, singleDeviceList);

View File

@ -53,12 +53,12 @@ public class ReceiptControllerTest {
@Before @Before
public void setup() throws Exception { public void setup() throws Exception {
Set<Device> singleDeviceList = new HashSet<Device>() {{ Set<Device> singleDeviceList = new HashSet<Device>() {{
add(new Device(1, null, "foo", "bar", "baz", "isgcm", null, null, false, 111, null, System.currentTimeMillis(), System.currentTimeMillis())); add(new Device(1, null, "foo", "bar", "baz", "isgcm", null, null, false, 111, null, System.currentTimeMillis(), System.currentTimeMillis(), false));
}}; }};
Set<Device> multiDeviceList = new HashSet<Device>() {{ Set<Device> multiDeviceList = new HashSet<Device>() {{
add(new Device(1, null, "foo", "bar", "baz", "isgcm", null, null, false, 222, null, System.currentTimeMillis(), System.currentTimeMillis())); add(new Device(1, null, "foo", "bar", "baz", "isgcm", null, null, false, 222, null, System.currentTimeMillis(), System.currentTimeMillis(), false));
add(new Device(2, null, "foo", "bar", "baz", "isgcm", null, null, false, 333, null, System.currentTimeMillis(), System.currentTimeMillis())); add(new Device(2, null, "foo", "bar", "baz", "isgcm", null, null, false, 333, null, System.currentTimeMillis(), System.currentTimeMillis(), false));
}}; }};
Account singleDeviceAccount = new Account(SINGLE_DEVICE_RECIPIENT, singleDeviceList); Account singleDeviceAccount = new Account(SINGLE_DEVICE_RECIPIENT, singleDeviceList);

View File

@ -14,9 +14,9 @@ public class ClientContactTest {
@Test @Test
public void serializeToJSON() throws Exception { public void serializeToJSON() throws Exception {
byte[] token = Util.getContactToken("+14152222222"); byte[] token = Util.getContactToken("+14152222222");
ClientContact contact = new ClientContact(token, null); ClientContact contact = new ClientContact(token, null, false);
ClientContact contactWithRelay = new ClientContact(token, "whisper"); ClientContact contactWithRelay = new ClientContact(token, "whisper", false);
ClientContact contactWithRelaySms = new ClientContact(token, "whisper"); ClientContact contactWithRelayVox = new ClientContact(token, "whisper", true);
assertThat("Basic Contact Serialization works", assertThat("Basic Contact Serialization works",
asJson(contact), asJson(contact),
@ -25,12 +25,16 @@ public class ClientContactTest {
assertThat("Contact Relay Serialization works", assertThat("Contact Relay Serialization works",
asJson(contactWithRelay), asJson(contactWithRelay),
is(equalTo(jsonFixture("fixtures/contact.relay.json")))); is(equalTo(jsonFixture("fixtures/contact.relay.json"))));
assertThat("Contact Relay Vox Serializaton works",
asJson(contactWithRelayVox),
is(equalTo(jsonFixture("fixtures/contact.relay.voice.json"))));
} }
@Test @Test
public void deserializeFromJSON() throws Exception { public void deserializeFromJSON() throws Exception {
ClientContact contact = new ClientContact(Util.getContactToken("+14152222222"), ClientContact contact = new ClientContact(Util.getContactToken("+14152222222"),
"whisper"); "whisper", false);
assertThat("a ClientContact can be deserialized from JSON", assertThat("a ClientContact can be deserialized from JSON",
fromJson(jsonFixture("fixtures/contact.relay.json"), ClientContact.class), fromJson(jsonFixture("fixtures/contact.relay.json"), ClientContact.class),

View File

@ -26,7 +26,7 @@ public class PreKeyTest {
@Test @Test
public void deserializeFromJSONV() throws Exception { public void deserializeFromJSONV() throws Exception {
ClientContact contact = new ClientContact(Util.getContactToken("+14152222222"), ClientContact contact = new ClientContact(Util.getContactToken("+14152222222"),
"whisper"); "whisper", false);
assertThat("a ClientContact can be deserialized from JSON", assertThat("a ClientContact can be deserialized from JSON",
fromJson(jsonFixture("fixtures/contact.relay.json"), ClientContact.class), fromJson(jsonFixture("fixtures/contact.relay.json"), ClientContact.class),

View File

@ -0,0 +1,5 @@
{
"token" : "BQVVHxMt5zAFXA",
"voice" : true,
"relay" : "whisper"
}