parent
f7132bdbbc
commit
45a0b74b89
|
@ -31,6 +31,7 @@ import org.whispersystems.textsecuregcm.storage.Account;
|
|||
import org.whispersystems.textsecuregcm.storage.AccountsManager;
|
||||
import org.whispersystems.textsecuregcm.storage.Device;
|
||||
import org.whispersystems.textsecuregcm.storage.PendingDevicesManager;
|
||||
import org.whispersystems.textsecuregcm.util.Util;
|
||||
import org.whispersystems.textsecuregcm.util.VerificationCode;
|
||||
|
||||
import javax.validation.Valid;
|
||||
|
@ -119,6 +120,7 @@ public class DeviceController {
|
|||
device.setSignalingKey(accountAttributes.getSignalingKey());
|
||||
device.setFetchesMessages(accountAttributes.getFetchesMessages());
|
||||
device.setId(account.get().getNextDeviceId());
|
||||
device.setLastSeen(Util.todayInMillis());
|
||||
|
||||
account.get().addDevice(device);
|
||||
accounts.update(account.get());
|
||||
|
|
|
@ -18,7 +18,7 @@ import javax.ws.rs.QueryParam;
|
|||
import javax.ws.rs.WebApplicationException;
|
||||
import javax.ws.rs.core.Response;
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import io.dropwizard.auth.Auth;
|
||||
import static org.whispersystems.textsecuregcm.entities.MessageProtos.OutgoingMessageSignal;
|
||||
|
@ -74,8 +74,8 @@ public class ReceiptController {
|
|||
private void sendDirectReceipt(Account source, String destination, long messageId)
|
||||
throws NotPushRegisteredException, TransientPushFailureException, NoSuchUserException
|
||||
{
|
||||
Account destinationAccount = getDestinationAccount(destination);
|
||||
List<Device> destinationDevices = destinationAccount.getDevices();
|
||||
Account destinationAccount = getDestinationAccount(destination);
|
||||
Set<Device> destinationDevices = destinationAccount.getDevices();
|
||||
|
||||
OutgoingMessageSignal.Builder message =
|
||||
OutgoingMessageSignal.newBuilder()
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
*/
|
||||
package org.whispersystems.textsecuregcm.entities;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import com.google.common.annotations.VisibleForTesting;
|
||||
|
||||
|
@ -42,7 +43,19 @@ public class PreKeyResponseV2 {
|
|||
}
|
||||
|
||||
@VisibleForTesting
|
||||
public List<PreKeyResponseItemV2> getDevices() {
|
||||
return devices;
|
||||
@JsonIgnore
|
||||
public PreKeyResponseItemV2 getDevice(int deviceId) {
|
||||
for (PreKeyResponseItemV2 device : devices) {
|
||||
if (device.getDeviceId() == deviceId) return device;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
@JsonIgnore
|
||||
public int getDevicesCount() {
|
||||
return devices.size();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -40,6 +40,6 @@ public class NonLimitedAccount extends Account {
|
|||
|
||||
@Override
|
||||
public Optional<Device> getAuthenticatedDevice() {
|
||||
return Optional.of(new Device(deviceId, null, null, null, null, null, false, 0, null));
|
||||
return Optional.of(new Device(deviceId, null, null, null, null, null, false, 0, null, System.currentTimeMillis()));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -22,8 +22,8 @@ import com.fasterxml.jackson.annotation.JsonProperty;
|
|||
import com.google.common.annotations.VisibleForTesting;
|
||||
import com.google.common.base.Optional;
|
||||
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
public class Account {
|
||||
|
||||
|
@ -36,7 +36,7 @@ public class Account {
|
|||
private boolean supportsSms;
|
||||
|
||||
@JsonProperty
|
||||
private List<Device> devices = new LinkedList<>();
|
||||
private Set<Device> devices = new HashSet<>();
|
||||
|
||||
@JsonProperty
|
||||
private String identityKey;
|
||||
|
@ -47,7 +47,7 @@ public class Account {
|
|||
public Account() {}
|
||||
|
||||
@VisibleForTesting
|
||||
public Account(String number, boolean supportsSms, List<Device> devices) {
|
||||
public Account(String number, boolean supportsSms, Set<Device> devices) {
|
||||
this.number = number;
|
||||
this.supportsSms = supportsSms;
|
||||
this.devices = devices;
|
||||
|
@ -78,14 +78,11 @@ public class Account {
|
|||
}
|
||||
|
||||
public void addDevice(Device device) {
|
||||
this.devices.remove(device);
|
||||
this.devices.add(device);
|
||||
}
|
||||
|
||||
public void setDevices(List<Device> devices) {
|
||||
this.devices = devices;
|
||||
}
|
||||
|
||||
public List<Device> getDevices() {
|
||||
public Set<Device> getDevices() {
|
||||
return devices;
|
||||
}
|
||||
|
||||
|
@ -113,7 +110,9 @@ public class Account {
|
|||
long highestDevice = Device.MASTER_ID;
|
||||
|
||||
for (Device device : devices) {
|
||||
if (device.getId() > highestDevice) {
|
||||
if (!device.isActive()) {
|
||||
return device.getId();
|
||||
} else if (device.getId() > highestDevice) {
|
||||
highestDevice = device.getId();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -22,6 +22,8 @@ import org.whispersystems.textsecuregcm.auth.AuthenticationCredentials;
|
|||
import org.whispersystems.textsecuregcm.entities.SignedPreKey;
|
||||
import org.whispersystems.textsecuregcm.util.Util;
|
||||
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
public class Device {
|
||||
|
||||
public static final long MASTER_ID = 1;
|
||||
|
@ -56,12 +58,15 @@ public class Device {
|
|||
@JsonProperty
|
||||
private SignedPreKey signedPreKey;
|
||||
|
||||
@JsonProperty
|
||||
private long lastSeen;
|
||||
|
||||
public Device() {}
|
||||
|
||||
public Device(long id, String authToken, String salt,
|
||||
String signalingKey, String gcmId, String apnId,
|
||||
boolean fetchesMessages, int registrationId,
|
||||
SignedPreKey signedPreKey)
|
||||
SignedPreKey signedPreKey, long lastSeen)
|
||||
{
|
||||
this.id = id;
|
||||
this.authToken = authToken;
|
||||
|
@ -72,6 +77,7 @@ public class Device {
|
|||
this.fetchesMessages = fetchesMessages;
|
||||
this.registrationId = registrationId;
|
||||
this.signedPreKey = signedPreKey;
|
||||
this.lastSeen = lastSeen;
|
||||
}
|
||||
|
||||
public String getApnId() {
|
||||
|
@ -86,6 +92,14 @@ public class Device {
|
|||
}
|
||||
}
|
||||
|
||||
public void setLastSeen(long lastSeen) {
|
||||
this.lastSeen = lastSeen;
|
||||
}
|
||||
|
||||
public long getLastSeen() {
|
||||
return lastSeen;
|
||||
}
|
||||
|
||||
public String getGcmId() {
|
||||
return gcmId;
|
||||
}
|
||||
|
@ -124,7 +138,10 @@ public class Device {
|
|||
}
|
||||
|
||||
public boolean isActive() {
|
||||
return fetchesMessages || !Util.isEmpty(getApnId()) || !Util.isEmpty(getGcmId());
|
||||
boolean hasChannel = fetchesMessages || !Util.isEmpty(getApnId()) || !Util.isEmpty(getGcmId());
|
||||
|
||||
return (id == MASTER_ID && hasChannel) ||
|
||||
(id != MASTER_ID && hasChannel && signedPreKey != null && lastSeen > (System.currentTimeMillis() - TimeUnit.DAYS.toMillis(30)));
|
||||
}
|
||||
|
||||
public boolean getFetchesMessages() {
|
||||
|
@ -158,4 +175,17 @@ public class Device {
|
|||
public long getPushTimestamp() {
|
||||
return pushTimestamp;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object other) {
|
||||
if (other == null || !(other instanceof Device)) return false;
|
||||
|
||||
Device that = (Device)other;
|
||||
return this.id == that.id;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return (int)this.id;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -21,6 +21,7 @@ import java.net.URLEncoder;
|
|||
import java.security.MessageDigest;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
public class Util {
|
||||
|
||||
|
@ -119,4 +120,8 @@ public class Util {
|
|||
Thread.sleep(i);
|
||||
} catch (InterruptedException ie) {}
|
||||
}
|
||||
|
||||
public static long todayInMillis() {
|
||||
return TimeUnit.DAYS.toMillis(TimeUnit.MILLISECONDS.toDays(System.currentTimeMillis()));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -9,6 +9,7 @@ import org.whispersystems.textsecuregcm.storage.AccountsManager;
|
|||
import org.whispersystems.textsecuregcm.storage.Device;
|
||||
import org.whispersystems.textsecuregcm.storage.PubSubManager;
|
||||
import org.whispersystems.textsecuregcm.storage.StoredMessages;
|
||||
import org.whispersystems.textsecuregcm.util.Util;
|
||||
import org.whispersystems.websocket.session.WebSocketSessionContext;
|
||||
import org.whispersystems.websocket.setup.WebSocketConnectListener;
|
||||
|
||||
|
@ -48,6 +49,11 @@ public class AuthenticatedConnectListener implements WebSocketConnectListener {
|
|||
return;
|
||||
}
|
||||
|
||||
if (device.get().getLastSeen() != Util.todayInMillis()) {
|
||||
device.get().setLastSeen(Util.todayInMillis());
|
||||
accountsManager.update(account.get());
|
||||
}
|
||||
|
||||
final WebSocketConnection connection = new WebSocketConnection(accountsManager, pushSender,
|
||||
storedMessages, pubSubManager,
|
||||
account.get(), device.get(),
|
||||
|
|
|
@ -27,8 +27,10 @@ import org.whispersystems.textsecuregcm.storage.Device;
|
|||
import org.whispersystems.textsecuregcm.tests.util.AuthHelper;
|
||||
|
||||
import javax.ws.rs.core.MediaType;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import io.dropwizard.testing.junit.ResourceTestRule;
|
||||
import static org.hamcrest.CoreMatchers.equalTo;
|
||||
|
@ -69,13 +71,13 @@ public class FederatedControllerTest {
|
|||
|
||||
@Before
|
||||
public void setup() throws Exception {
|
||||
List<Device> singleDeviceList = new LinkedList<Device>() {{
|
||||
add(new Device(1, "foo", "bar", "baz", "isgcm", null, false, 111, null));
|
||||
Set<Device> singleDeviceList = new HashSet<Device>() {{
|
||||
add(new Device(1, "foo", "bar", "baz", "isgcm", null, false, 111, null, System.currentTimeMillis()));
|
||||
}};
|
||||
|
||||
List<Device> multiDeviceList = new LinkedList<Device>() {{
|
||||
add(new Device(1, "foo", "bar", "baz", "isgcm", null, false, 222, null));
|
||||
add(new Device(2, "foo", "bar", "baz", "isgcm", null, false, 333, null));
|
||||
Set<Device> multiDeviceList = new HashSet<Device>() {{
|
||||
add(new Device(1, "foo", "bar", "baz", "isgcm", null, false, 222, null, System.currentTimeMillis()));
|
||||
add(new Device(2, "foo", "bar", "baz", "isgcm", null, false, 333, null, System.currentTimeMillis()));
|
||||
}};
|
||||
|
||||
Account singleDeviceAccount = new Account(SINGLE_DEVICE_RECIPIENT, false, singleDeviceList);
|
||||
|
|
|
@ -26,8 +26,10 @@ import org.whispersystems.textsecuregcm.storage.Keys;
|
|||
import org.whispersystems.textsecuregcm.tests.util.AuthHelper;
|
||||
|
||||
import javax.ws.rs.core.MediaType;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import io.dropwizard.testing.junit.ResourceTestRule;
|
||||
import static org.fest.assertions.api.Assertions.assertThat;
|
||||
|
@ -73,7 +75,7 @@ public class KeyControllerTest {
|
|||
final Device sampleDevice3 = mock(Device.class);
|
||||
final Device sampleDevice4 = mock(Device.class);
|
||||
|
||||
List<Device> allDevices = new LinkedList<Device>() {{
|
||||
Set<Device> allDevices = new HashSet<Device>() {{
|
||||
add(sampleDevice);
|
||||
add(sampleDevice2);
|
||||
add(sampleDevice3);
|
||||
|
@ -198,10 +200,10 @@ public class KeyControllerTest {
|
|||
.get(PreKeyResponseV2.class);
|
||||
|
||||
assertThat(result.getIdentityKey()).isEqualTo(existsAccount.getIdentityKey());
|
||||
assertThat(result.getDevices().size()).isEqualTo(1);
|
||||
assertThat(result.getDevices().get(0).getPreKey().getKeyId()).isEqualTo(SAMPLE_KEY.getKeyId());
|
||||
assertThat(result.getDevices().get(0).getPreKey().getPublicKey()).isEqualTo(SAMPLE_KEY.getPublicKey());
|
||||
assertThat(result.getDevices().get(0).getSignedPreKey()).isEqualTo(existsAccount.getDevice(1).get().getSignedPreKey());
|
||||
assertThat(result.getDevicesCount()).isEqualTo(1);
|
||||
assertThat(result.getDevice(1).getPreKey().getKeyId()).isEqualTo(SAMPLE_KEY.getKeyId());
|
||||
assertThat(result.getDevice(1).getPreKey().getPublicKey()).isEqualTo(SAMPLE_KEY.getPublicKey());
|
||||
assertThat(result.getDevice(1).getSignedPreKey()).isEqualTo(existsAccount.getDevice(1).get().getSignedPreKey());
|
||||
|
||||
verify(keys).get(eq(EXISTS_NUMBER), eq(1L));
|
||||
verifyNoMoreInteractions(keys);
|
||||
|
@ -245,13 +247,13 @@ public class KeyControllerTest {
|
|||
.header("Authorization", AuthHelper.getAuthHeader(AuthHelper.VALID_NUMBER, AuthHelper.VALID_PASSWORD))
|
||||
.get(PreKeyResponseV2.class);
|
||||
|
||||
assertThat(results.getDevices().size()).isEqualTo(3);
|
||||
assertThat(results.getDevicesCount()).isEqualTo(3);
|
||||
assertThat(results.getIdentityKey()).isEqualTo(existsAccount.getIdentityKey());
|
||||
|
||||
PreKeyV2 signedPreKey = results.getDevices().get(0).getSignedPreKey();
|
||||
PreKeyV2 preKey = results.getDevices().get(0).getPreKey();
|
||||
long registrationId = results.getDevices().get(0).getRegistrationId();
|
||||
long deviceId = results.getDevices().get(0).getDeviceId();
|
||||
PreKeyV2 signedPreKey = results.getDevice(1).getSignedPreKey();
|
||||
PreKeyV2 preKey = results.getDevice(1).getPreKey();
|
||||
long registrationId = results.getDevice(1).getRegistrationId();
|
||||
long deviceId = results.getDevice(1).getDeviceId();
|
||||
|
||||
assertThat(preKey.getKeyId()).isEqualTo(SAMPLE_KEY.getKeyId());
|
||||
assertThat(preKey.getPublicKey()).isEqualTo(SAMPLE_KEY.getPublicKey());
|
||||
|
@ -260,10 +262,10 @@ public class KeyControllerTest {
|
|||
assertThat(signedPreKey.getPublicKey()).isEqualTo(SAMPLE_SIGNED_KEY.getPublicKey());
|
||||
assertThat(deviceId).isEqualTo(1);
|
||||
|
||||
signedPreKey = results.getDevices().get(1).getSignedPreKey();
|
||||
preKey = results.getDevices().get(1).getPreKey();
|
||||
registrationId = results.getDevices().get(1).getRegistrationId();
|
||||
deviceId = results.getDevices().get(1).getDeviceId();
|
||||
signedPreKey = results.getDevice(2).getSignedPreKey();
|
||||
preKey = results.getDevice(2).getPreKey();
|
||||
registrationId = results.getDevice(2).getRegistrationId();
|
||||
deviceId = results.getDevice(2).getDeviceId();
|
||||
|
||||
assertThat(preKey.getKeyId()).isEqualTo(SAMPLE_KEY2.getKeyId());
|
||||
assertThat(preKey.getPublicKey()).isEqualTo(SAMPLE_KEY2.getPublicKey());
|
||||
|
@ -272,10 +274,10 @@ public class KeyControllerTest {
|
|||
assertThat(signedPreKey.getPublicKey()).isEqualTo(SAMPLE_SIGNED_KEY2.getPublicKey());
|
||||
assertThat(deviceId).isEqualTo(2);
|
||||
|
||||
signedPreKey = results.getDevices().get(2).getSignedPreKey();
|
||||
preKey = results.getDevices().get(2).getPreKey();
|
||||
registrationId = results.getDevices().get(2).getRegistrationId();
|
||||
deviceId = results.getDevices().get(2).getDeviceId();
|
||||
signedPreKey = results.getDevice(4).getSignedPreKey();
|
||||
preKey = results.getDevice(4).getPreKey();
|
||||
registrationId = results.getDevice(4).getRegistrationId();
|
||||
deviceId = results.getDevice(4).getDeviceId();
|
||||
|
||||
assertThat(preKey.getKeyId()).isEqualTo(SAMPLE_KEY4.getKeyId());
|
||||
assertThat(preKey.getPublicKey()).isEqualTo(SAMPLE_KEY4.getPublicKey());
|
||||
|
|
|
@ -6,10 +6,13 @@ import com.sun.jersey.api.client.ClientResponse;
|
|||
import org.junit.Before;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.whispersystems.textsecuregcm.controllers.MessageController;
|
||||
import org.whispersystems.textsecuregcm.entities.IncomingMessageList;
|
||||
import org.whispersystems.textsecuregcm.entities.MessageProtos;
|
||||
import org.whispersystems.textsecuregcm.entities.MismatchedDevices;
|
||||
import org.whispersystems.textsecuregcm.entities.SignedPreKey;
|
||||
import org.whispersystems.textsecuregcm.entities.StaleDevices;
|
||||
import org.whispersystems.textsecuregcm.federation.FederatedClientManager;
|
||||
import org.whispersystems.textsecuregcm.limits.RateLimiter;
|
||||
|
@ -21,8 +24,9 @@ import org.whispersystems.textsecuregcm.storage.Device;
|
|||
import org.whispersystems.textsecuregcm.tests.util.AuthHelper;
|
||||
|
||||
import javax.ws.rs.core.MediaType;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import io.dropwizard.testing.junit.ResourceTestRule;
|
||||
import static org.hamcrest.CoreMatchers.equalTo;
|
||||
|
@ -57,13 +61,14 @@ public class MessageControllerTest {
|
|||
|
||||
@Before
|
||||
public void setup() throws Exception {
|
||||
List<Device> singleDeviceList = new LinkedList<Device>() {{
|
||||
add(new Device(1, "foo", "bar", "baz", "isgcm", null, false, 111, null));
|
||||
Set<Device> singleDeviceList = new HashSet<Device>() {{
|
||||
add(new Device(1, "foo", "bar", "baz", "isgcm", null, false, 111, null, System.currentTimeMillis()));
|
||||
}};
|
||||
|
||||
List<Device> multiDeviceList = new LinkedList<Device>() {{
|
||||
add(new Device(1, "foo", "bar", "baz", "isgcm", null, false, 222, null));
|
||||
add(new Device(2, "foo", "bar", "baz", "isgcm", null, false, 333, null));
|
||||
Set<Device> multiDeviceList = new HashSet<Device>() {{
|
||||
add(new Device(1, "foo", "bar", "baz", "isgcm", null, false, 222, new SignedPreKey(111, "foo", "bar"), System.currentTimeMillis()));
|
||||
add(new Device(2, "foo", "bar", "baz", "isgcm", null, false, 333, new SignedPreKey(222, "oof", "rab"), System.currentTimeMillis()));
|
||||
add(new Device(3, "foo", "bar", "baz", "isgcm", null, false, 444, null, System.currentTimeMillis() - TimeUnit.DAYS.toMillis(31)));
|
||||
}};
|
||||
|
||||
Account singleDeviceAccount = new Account(SINGLE_DEVICE_RECIPIENT, false, singleDeviceList);
|
||||
|
|
|
@ -18,8 +18,11 @@ import org.whispersystems.textsecuregcm.storage.AccountsManager;
|
|||
import org.whispersystems.textsecuregcm.storage.Device;
|
||||
import org.whispersystems.textsecuregcm.tests.util.AuthHelper;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import io.dropwizard.testing.junit.ResourceTestRule;
|
||||
import static org.fest.assertions.api.Assertions.assertThat;
|
||||
|
@ -47,13 +50,13 @@ public class ReceiptControllerTest {
|
|||
|
||||
@Before
|
||||
public void setup() throws Exception {
|
||||
List<Device> singleDeviceList = new LinkedList<Device>() {{
|
||||
add(new Device(1, "foo", "bar", "baz", "isgcm", null, false, 111, null));
|
||||
Set<Device> singleDeviceList = new HashSet<Device>() {{
|
||||
add(new Device(1, "foo", "bar", "baz", "isgcm", null, false, 111, null, System.currentTimeMillis()));
|
||||
}};
|
||||
|
||||
List<Device> multiDeviceList = new LinkedList<Device>() {{
|
||||
add(new Device(1, "foo", "bar", "baz", "isgcm", null, false, 222, null));
|
||||
add(new Device(2, "foo", "bar", "baz", "isgcm", null, false, 333, null));
|
||||
Set<Device> multiDeviceList = new HashSet<Device>() {{
|
||||
add(new Device(1, "foo", "bar", "baz", "isgcm", null, false, 222, null, System.currentTimeMillis()));
|
||||
add(new Device(2, "foo", "bar", "baz", "isgcm", null, false, 333, null, System.currentTimeMillis()));
|
||||
}};
|
||||
|
||||
Account singleDeviceAccount = new Account(SINGLE_DEVICE_RECIPIENT, false, singleDeviceList);
|
||||
|
|
|
@ -25,8 +25,10 @@ import org.whispersystems.websocket.session.WebSocketSessionContext;
|
|||
|
||||
import java.io.IOException;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import io.dropwizard.auth.basic.BasicCredentials;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
@ -127,7 +129,7 @@ public class WebSocketConnectionTest {
|
|||
|
||||
final Device sender1device = mock(Device.class);
|
||||
|
||||
List<Device> sender1devices = new LinkedList<Device>() {{
|
||||
Set<Device> sender1devices = new HashSet<Device>() {{
|
||||
add(sender1device);
|
||||
}};
|
||||
|
||||
|
|
Loading…
Reference in New Issue