From c623f70caa3daa8bfdcf946af03c195cf10ee3be Mon Sep 17 00:00:00 2001 From: Moxie Marlinspike Date: Mon, 23 Sep 2019 12:01:12 -0700 Subject: [PATCH] Add support for capabilities --- .../controllers/AccountController.java | 4 +- .../controllers/DeviceController.java | 11 +++++- .../controllers/ProfileController.java | 6 ++- .../entities/AccountAttributes.java | 9 +++++ .../textsecuregcm/entities/Profile.java | 18 ++++++--- .../entities/UserCapabilities.java | 18 +++++++++ .../textsecuregcm/storage/Account.java | 9 ++--- .../textsecuregcm/storage/Device.java | 39 +++++++++++++------ .../controllers/MessageControllerTest.java | 8 ++-- .../controllers/ProfileControllerTest.java | 25 ++++++++++++ .../tests/storage/AccountTest.java | 37 ++++++++++++++++++ .../tests/storage/AccountsTest.java | 2 +- 12 files changed, 154 insertions(+), 32 deletions(-) create mode 100644 service/src/main/java/org/whispersystems/textsecuregcm/entities/UserCapabilities.java diff --git a/service/src/main/java/org/whispersystems/textsecuregcm/controllers/AccountController.java b/service/src/main/java/org/whispersystems/textsecuregcm/controllers/AccountController.java index a0cba8fa9..01cda5d09 100644 --- a/service/src/main/java/org/whispersystems/textsecuregcm/controllers/AccountController.java +++ b/service/src/main/java/org/whispersystems/textsecuregcm/controllers/AccountController.java @@ -487,7 +487,7 @@ public class AccountController { device.setFetchesMessages(attributes.getFetchesMessages()); device.setName(attributes.getName()); device.setLastSeen(Util.todayInMillis()); - device.setUnauthenticatedDeliverySupported(attributes.getUnidentifiedAccessKey() != null); + device.setCapabilities(attributes.getCapabilities()); device.setRegistrationId(attributes.getRegistrationId()); device.setSignalingKey(attributes.getSignalingKey()); device.setUserAgent(userAgent); @@ -599,7 +599,7 @@ public class AccountController { device.setFetchesMessages(accountAttributes.getFetchesMessages()); device.setRegistrationId(accountAttributes.getRegistrationId()); device.setName(accountAttributes.getName()); - device.setUnauthenticatedDeliverySupported(accountAttributes.getUnidentifiedAccessKey() != null); + device.setCapabilities(accountAttributes.getCapabilities()); device.setCreated(System.currentTimeMillis()); device.setLastSeen(Util.todayInMillis()); device.setUserAgent(userAgent); diff --git a/service/src/main/java/org/whispersystems/textsecuregcm/controllers/DeviceController.java b/service/src/main/java/org/whispersystems/textsecuregcm/controllers/DeviceController.java index 5746f5f45..f3f0398a2 100644 --- a/service/src/main/java/org/whispersystems/textsecuregcm/controllers/DeviceController.java +++ b/service/src/main/java/org/whispersystems/textsecuregcm/controllers/DeviceController.java @@ -33,6 +33,7 @@ import org.whispersystems.textsecuregcm.sqs.DirectoryQueue; import org.whispersystems.textsecuregcm.storage.Account; import org.whispersystems.textsecuregcm.storage.AccountsManager; import org.whispersystems.textsecuregcm.storage.Device; +import org.whispersystems.textsecuregcm.storage.Device.DeviceCapabilities; import org.whispersystems.textsecuregcm.storage.MessagesManager; import org.whispersystems.textsecuregcm.storage.PendingDevicesManager; import org.whispersystems.textsecuregcm.util.Util; @@ -221,7 +222,15 @@ public class DeviceController { @Path("/unauthenticated_delivery") public void setUnauthenticatedDelivery(@Auth Account account) { assert(account.getAuthenticatedDevice().isPresent()); - account.getAuthenticatedDevice().get().setUnauthenticatedDeliverySupported(true); + // Deprecated + } + + @Timed + @PUT + @Path("/capabilities") + public void setCapabiltities(@Auth Account account, @Valid DeviceCapabilities capabilities) { + assert(account.getAuthenticatedDevice().isPresent()); + account.getAuthenticatedDevice().get().setCapabilities(capabilities); accounts.update(account); } diff --git a/service/src/main/java/org/whispersystems/textsecuregcm/controllers/ProfileController.java b/service/src/main/java/org/whispersystems/textsecuregcm/controllers/ProfileController.java index cac806dd0..273ca631b 100644 --- a/service/src/main/java/org/whispersystems/textsecuregcm/controllers/ProfileController.java +++ b/service/src/main/java/org/whispersystems/textsecuregcm/controllers/ProfileController.java @@ -17,6 +17,7 @@ import org.whispersystems.textsecuregcm.auth.UnidentifiedAccessChecksum; import org.whispersystems.textsecuregcm.configuration.CdnConfiguration; import org.whispersystems.textsecuregcm.entities.Profile; import org.whispersystems.textsecuregcm.entities.ProfileAvatarUploadAttributes; +import org.whispersystems.textsecuregcm.entities.UserCapabilities; import org.whispersystems.textsecuregcm.limits.RateLimiters; import org.whispersystems.textsecuregcm.s3.PolicySigner; import org.whispersystems.textsecuregcm.s3.PostPolicyGenerator; @@ -102,8 +103,9 @@ public class ProfileController { return new Profile(accountProfile.get().getProfileName(), accountProfile.get().getAvatar(), accountProfile.get().getIdentityKey(), - accountProfile.get().isUnauthenticatedDeliverySupported() ? UnidentifiedAccessChecksum.generateFor(accountProfile.get().getUnidentifiedAccessKey()) : null, - accountProfile.get().isUnrestrictedUnidentifiedAccess()); + UnidentifiedAccessChecksum.generateFor(accountProfile.get().getUnidentifiedAccessKey()), + accountProfile.get().isUnrestrictedUnidentifiedAccess(), + new UserCapabilities(accountProfile.get().isUuidAddressingSupported())); } @Timed diff --git a/service/src/main/java/org/whispersystems/textsecuregcm/entities/AccountAttributes.java b/service/src/main/java/org/whispersystems/textsecuregcm/entities/AccountAttributes.java index 9359fab3a..99bf21efb 100644 --- a/service/src/main/java/org/whispersystems/textsecuregcm/entities/AccountAttributes.java +++ b/service/src/main/java/org/whispersystems/textsecuregcm/entities/AccountAttributes.java @@ -19,6 +19,8 @@ package org.whispersystems.textsecuregcm.entities; import com.fasterxml.jackson.annotation.JsonProperty; import com.google.common.annotations.VisibleForTesting; import org.hibernate.validator.constraints.Length; +import org.whispersystems.textsecuregcm.storage.Device; +import org.whispersystems.textsecuregcm.storage.Device.DeviceCapabilities; public class AccountAttributes { @@ -47,6 +49,9 @@ public class AccountAttributes { @JsonProperty private boolean unrestrictedUnidentifiedAccess; + @JsonProperty + private DeviceCapabilities capabilities; + public AccountAttributes() {} @VisibleForTesting @@ -95,4 +100,8 @@ public class AccountAttributes { public boolean isUnrestrictedUnidentifiedAccess() { return unrestrictedUnidentifiedAccess; } + + public DeviceCapabilities getCapabilities() { + return capabilities; + } } diff --git a/service/src/main/java/org/whispersystems/textsecuregcm/entities/Profile.java b/service/src/main/java/org/whispersystems/textsecuregcm/entities/Profile.java index 9dec49ae1..be97f59b1 100644 --- a/service/src/main/java/org/whispersystems/textsecuregcm/entities/Profile.java +++ b/service/src/main/java/org/whispersystems/textsecuregcm/entities/Profile.java @@ -3,10 +3,6 @@ package org.whispersystems.textsecuregcm.entities; import com.fasterxml.jackson.annotation.JsonProperty; import com.google.common.annotations.VisibleForTesting; -import org.hibernate.validator.constraints.Length; - -import javax.validation.constraints.Max; - public class Profile { @JsonProperty @@ -24,14 +20,21 @@ public class Profile { @JsonProperty private boolean unrestrictedUnidentifiedAccess; + @JsonProperty + private UserCapabilities capabilities; + public Profile() {} - public Profile(String name, String avatar, String identityKey, String unidentifiedAccess, boolean unrestrictedUnidentifiedAccess) { + public Profile(String name, String avatar, String identityKey, + String unidentifiedAccess, boolean unrestrictedUnidentifiedAccess, + UserCapabilities capabilities) + { this.name = name; this.avatar = avatar; this.identityKey = identityKey; this.unidentifiedAccess = unidentifiedAccess; this.unrestrictedUnidentifiedAccess = unrestrictedUnidentifiedAccess; + this.capabilities = capabilities; } @VisibleForTesting @@ -59,4 +62,9 @@ public class Profile { return unrestrictedUnidentifiedAccess; } + @VisibleForTesting + public UserCapabilities getCapabilities() { + return capabilities; + } + } diff --git a/service/src/main/java/org/whispersystems/textsecuregcm/entities/UserCapabilities.java b/service/src/main/java/org/whispersystems/textsecuregcm/entities/UserCapabilities.java new file mode 100644 index 000000000..2510dc6aa --- /dev/null +++ b/service/src/main/java/org/whispersystems/textsecuregcm/entities/UserCapabilities.java @@ -0,0 +1,18 @@ +package org.whispersystems.textsecuregcm.entities; + +import com.fasterxml.jackson.annotation.JsonProperty; + +public class UserCapabilities { + @JsonProperty + private boolean uuid; + + public UserCapabilities() {} + + public UserCapabilities(boolean uuid) { + this.uuid = uuid; + } + + public boolean isUuid() { + return uuid; + } +} diff --git a/service/src/main/java/org/whispersystems/textsecuregcm/storage/Account.java b/service/src/main/java/org/whispersystems/textsecuregcm/storage/Account.java index f4a612ded..f94aab91b 100644 --- a/service/src/main/java/org/whispersystems/textsecuregcm/storage/Account.java +++ b/service/src/main/java/org/whispersystems/textsecuregcm/storage/Account.java @@ -20,7 +20,6 @@ package org.whispersystems.textsecuregcm.storage; import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.annotation.JsonProperty; import com.google.common.annotations.VisibleForTesting; - import org.whispersystems.textsecuregcm.auth.AmbiguousIdentifier; import javax.security.auth.Subject; @@ -33,8 +32,6 @@ import java.util.concurrent.TimeUnit; public class Account implements Principal { - static final int MEMCACHE_VERION = 5; - @JsonIgnore private UUID uuid; @@ -114,7 +111,7 @@ public class Account implements Principal { } public void removeDevice(long deviceId) { - this.devices.remove(new Device(deviceId, null, null, null, null, null, null, null, false, 0, null, 0, 0, "NA", false, 0)); + this.devices.remove(new Device(deviceId, null, null, null, null, null, null, null, false, 0, null, 0, 0, "NA", 0, null)); } public Set getDevices() { @@ -135,8 +132,8 @@ public class Account implements Principal { return Optional.empty(); } - public boolean isUnauthenticatedDeliverySupported() { - return devices.stream().filter(Device::isEnabled).allMatch(Device::isUnauthenticatedDeliverySupported); + public boolean isUuidAddressingSupported() { + return devices.stream().filter(Device::isEnabled).allMatch(device -> device.getCapabilities().isUuid()); } public boolean isEnabled() { diff --git a/service/src/main/java/org/whispersystems/textsecuregcm/storage/Device.java b/service/src/main/java/org/whispersystems/textsecuregcm/storage/Device.java index 339c9e653..e1d672d32 100644 --- a/service/src/main/java/org/whispersystems/textsecuregcm/storage/Device.java +++ b/service/src/main/java/org/whispersystems/textsecuregcm/storage/Device.java @@ -19,6 +19,7 @@ package org.whispersystems.textsecuregcm.storage; import com.fasterxml.jackson.annotation.JsonProperty; import org.whispersystems.textsecuregcm.auth.AuthenticationCredentials; +import org.whispersystems.textsecuregcm.entities.UserCapabilities; import org.whispersystems.textsecuregcm.entities.SignedPreKey; import org.whispersystems.textsecuregcm.util.Util; @@ -77,7 +78,7 @@ public class Device { private String userAgent; @JsonProperty - private boolean unauthenticatedDelivery; + private DeviceCapabilities capabilities; public Device() {} @@ -86,7 +87,7 @@ public class Device { String voipApnId, boolean fetchesMessages, int registrationId, SignedPreKey signedPreKey, long lastSeen, long created, String userAgent, - boolean unauthenticatedDelivery, long uninstalledFeedback) + long uninstalledFeedback, DeviceCapabilities capabilities) { this.id = id; this.name = name; @@ -102,8 +103,8 @@ public class Device { this.lastSeen = lastSeen; this.created = created; this.userAgent = userAgent; - this.unauthenticatedDelivery = unauthenticatedDelivery; this.uninstalledFeedback = uninstalledFeedback; + this.capabilities = capabilities; } public String getApnId() { @@ -178,14 +179,6 @@ public class Device { this.name = name; } - public boolean isUnauthenticatedDeliverySupported() { - return unauthenticatedDelivery; - } - - public void setUnauthenticatedDeliverySupported(boolean unauthenticatedDelivery) { - this.unauthenticatedDelivery = unauthenticatedDelivery; - } - public void setAuthenticationCredentials(AuthenticationCredentials credentials) { this.authToken = credentials.getHashedAuthenticationToken(); this.salt = credentials.getSalt(); @@ -195,6 +188,14 @@ public class Device { return new AuthenticationCredentials(authToken, salt); } + public DeviceCapabilities getCapabilities() { + return capabilities; + } + + public void setCapabilities(DeviceCapabilities capabilities) { + this.capabilities = capabilities; + } + public String getSignalingKey() { return signalingKey; } @@ -262,4 +263,20 @@ public class Device { public int hashCode() { return (int)this.id; } + + public static class DeviceCapabilities { + @JsonProperty + private boolean uuid; + + public DeviceCapabilities() {} + + public DeviceCapabilities(boolean uuid) { + this.uuid = uuid; + } + + public boolean isUuid() { + return uuid; + } + } + } diff --git a/service/src/test/java/org/whispersystems/textsecuregcm/tests/controllers/MessageControllerTest.java b/service/src/test/java/org/whispersystems/textsecuregcm/tests/controllers/MessageControllerTest.java index 17114d058..ccc844550 100644 --- a/service/src/test/java/org/whispersystems/textsecuregcm/tests/controllers/MessageControllerTest.java +++ b/service/src/test/java/org/whispersystems/textsecuregcm/tests/controllers/MessageControllerTest.java @@ -85,13 +85,13 @@ public class MessageControllerTest { @Before public void setup() throws Exception { Set singleDeviceList = new HashSet() {{ - add(new Device(1, null, "foo", "bar", "baz", "isgcm", null, null, false, 111, new SignedPreKey(333, "baz", "boop"), System.currentTimeMillis(), System.currentTimeMillis(), "Test", true, 0)); + add(new Device(1, null, "foo", "bar", "baz", "isgcm", null, null, false, 111, new SignedPreKey(333, "baz", "boop"), System.currentTimeMillis(), System.currentTimeMillis(), "Test", 0, new Device.DeviceCapabilities(true))); }}; Set multiDeviceList = new HashSet() {{ - add(new Device(1, null, "foo", "bar", "baz", "isgcm", null, null, false, 222, new SignedPreKey(111, "foo", "bar"), System.currentTimeMillis(), System.currentTimeMillis(), "Test", true, 0)); - add(new Device(2, null, "foo", "bar", "baz", "isgcm", null, null, false, 333, new SignedPreKey(222, "oof", "rab"), System.currentTimeMillis(), System.currentTimeMillis(), "Test", true, 0)); - add(new Device(3, null, "foo", "bar", "baz", "isgcm", null, null, false, 444, null, System.currentTimeMillis() - TimeUnit.DAYS.toMillis(31), System.currentTimeMillis(), "Test", true, 0)); + add(new Device(1, null, "foo", "bar", "baz", "isgcm", null, null, false, 222, new SignedPreKey(111, "foo", "bar"), System.currentTimeMillis(), System.currentTimeMillis(), "Test", 0, new Device.DeviceCapabilities(true))); + add(new Device(2, null, "foo", "bar", "baz", "isgcm", null, null, false, 333, new SignedPreKey(222, "oof", "rab"), System.currentTimeMillis(), System.currentTimeMillis(), "Test", 0, new Device.DeviceCapabilities(true))); + add(new Device(3, null, "foo", "bar", "baz", "isgcm", null, null, false, 444, null, System.currentTimeMillis() - TimeUnit.DAYS.toMillis(31), System.currentTimeMillis(), "Test", 0, new Device.DeviceCapabilities(false))); }}; Account singleDeviceAccount = new Account(SINGLE_DEVICE_RECIPIENT, SINGLE_DEVICE_UUID, singleDeviceList, "1234".getBytes()); diff --git a/service/src/test/java/org/whispersystems/textsecuregcm/tests/controllers/ProfileControllerTest.java b/service/src/test/java/org/whispersystems/textsecuregcm/tests/controllers/ProfileControllerTest.java index a03d3e164..c2372c928 100644 --- a/service/src/test/java/org/whispersystems/textsecuregcm/tests/controllers/ProfileControllerTest.java +++ b/service/src/test/java/org/whispersystems/textsecuregcm/tests/controllers/ProfileControllerTest.java @@ -63,9 +63,22 @@ public class ProfileControllerTest { when(profileAccount.getAvatar()).thenReturn("profiles/bang"); when(profileAccount.getAvatarDigest()).thenReturn("buh"); when(profileAccount.isEnabled()).thenReturn(true); + when(profileAccount.isUuidAddressingSupported()).thenReturn(false); + + Account capabilitiesAccount = mock(Account.class); + + when(capabilitiesAccount.getIdentityKey()).thenReturn("barz"); + when(capabilitiesAccount.getProfileName()).thenReturn("bazz"); + when(capabilitiesAccount.getAvatar()).thenReturn("profiles/bangz"); + when(capabilitiesAccount.getAvatarDigest()).thenReturn("buz"); + when(capabilitiesAccount.isEnabled()).thenReturn(true); + when(capabilitiesAccount.isUuidAddressingSupported()).thenReturn(true); when(accountsManager.get(AuthHelper.VALID_NUMBER_TWO)).thenReturn(Optional.of(profileAccount)); when(accountsManager.get(argThat((ArgumentMatcher) identifier -> identifier != null && identifier.hasNumber() && identifier.getNumber().equals(AuthHelper.VALID_NUMBER_TWO)))).thenReturn(Optional.of(profileAccount)); + + when(accountsManager.get(AuthHelper.VALID_NUMBER)).thenReturn(Optional.of(capabilitiesAccount)); + when(accountsManager.get(argThat((ArgumentMatcher) identifier -> identifier != null && identifier.hasNumber() && identifier.getNumber().equals(AuthHelper.VALID_NUMBER)))).thenReturn(Optional.of(capabilitiesAccount)); } @@ -80,6 +93,7 @@ public class ProfileControllerTest { assertThat(profile.getIdentityKey()).isEqualTo("bar"); assertThat(profile.getName()).isEqualTo("baz"); assertThat(profile.getAvatar()).isEqualTo("profiles/bang"); + assertThat(profile.getCapabilities().isUuid()).isFalse(); verify(accountsManager, times(1)).get(argThat((ArgumentMatcher) identifier -> identifier != null && identifier.hasNumber() && identifier.getNumber().equals(AuthHelper.VALID_NUMBER_TWO))); verify(rateLimiters, times(1)).getProfileLimiter(); @@ -107,4 +121,15 @@ public class ProfileControllerTest { assertThat(response.getStatus()).isEqualTo(401); } + @Test + public void testProfileCapabilities() throws Exception { + Profile profile= resources.getJerseyTest() + .target("/v1/profile/" + AuthHelper.VALID_NUMBER) + .request() + .header("Authorization", AuthHelper.getAuthHeader(AuthHelper.VALID_NUMBER, AuthHelper.VALID_PASSWORD)) + .get(Profile.class); + + assertThat(profile.getCapabilities().isUuid()).isTrue(); + } + } diff --git a/service/src/test/java/org/whispersystems/textsecuregcm/tests/storage/AccountTest.java b/service/src/test/java/org/whispersystems/textsecuregcm/tests/storage/AccountTest.java index 9a4e4d549..04c8e03fb 100644 --- a/service/src/test/java/org/whispersystems/textsecuregcm/tests/storage/AccountTest.java +++ b/service/src/test/java/org/whispersystems/textsecuregcm/tests/storage/AccountTest.java @@ -23,6 +23,10 @@ public class AccountTest { private final Device recentSecondaryDevice = mock(Device.class); private final Device oldSecondaryDevice = mock(Device.class); + private final Device uuidCapableDevice = mock(Device.class); + private final Device uuidIncapableDevice = mock(Device.class); + private final Device uuidIncapableExpiredDevice = mock(Device.class); + @Before public void setup() { when(oldMasterDevice.getLastSeen()).thenReturn(System.currentTimeMillis() - TimeUnit.DAYS.toMillis(366)); @@ -44,6 +48,18 @@ public class AccountTest { when(oldSecondaryDevice.getLastSeen()).thenReturn(System.currentTimeMillis() - TimeUnit.DAYS.toMillis(366)); when(oldSecondaryDevice.isEnabled()).thenReturn(false); when(oldSecondaryDevice.getId()).thenReturn(2L); + + when(uuidCapableDevice.getCapabilities()).thenReturn(new Device.DeviceCapabilities(true)); + when(uuidCapableDevice.getLastSeen()).thenReturn(System.currentTimeMillis() - TimeUnit.DAYS.toMillis(1)); + when(uuidCapableDevice.isEnabled()).thenReturn(true); + + when(uuidIncapableDevice.getCapabilities()).thenReturn(new Device.DeviceCapabilities(false)); + when(uuidIncapableDevice.getLastSeen()).thenReturn(System.currentTimeMillis() - TimeUnit.DAYS.toMillis(1)); + when(uuidIncapableDevice.isEnabled()).thenReturn(true); + + when(uuidIncapableExpiredDevice.getCapabilities()).thenReturn(new Device.DeviceCapabilities(false)); + when(uuidIncapableExpiredDevice.getLastSeen()).thenReturn(System.currentTimeMillis() - TimeUnit.DAYS.toMillis(31)); + when(uuidIncapableExpiredDevice.isEnabled()).thenReturn(false); } @Test @@ -80,4 +96,25 @@ public class AccountTest { assertFalse(oldPrimaryAccount.isEnabled()); } + @Test + public void testCapabilities() { + Account uuidCapable = new Account("+14152222222", UUID.randomUUID(), new HashSet() {{ + add(uuidCapableDevice); + }}, "1234".getBytes()); + + Account uuidIncapable = new Account("+14152222222", UUID.randomUUID(), new HashSet() {{ + add(uuidCapableDevice); + add(uuidIncapableDevice); + }}, "1234".getBytes()); + + Account uuidCapableWithExpiredIncapable = new Account("+14152222222", UUID.randomUUID(), new HashSet() {{ + add(uuidCapableDevice); + add(uuidIncapableExpiredDevice); + }}, "1234".getBytes()); + + assertTrue(uuidCapable.isUuidAddressingSupported()); + assertFalse(uuidIncapable.isUuidAddressingSupported()); + assertTrue(uuidCapableWithExpiredIncapable.isUuidAddressingSupported()); + } + } diff --git a/service/src/test/java/org/whispersystems/textsecuregcm/tests/storage/AccountsTest.java b/service/src/test/java/org/whispersystems/textsecuregcm/tests/storage/AccountsTest.java index c8c893d28..ed11f6686 100644 --- a/service/src/test/java/org/whispersystems/textsecuregcm/tests/storage/AccountsTest.java +++ b/service/src/test/java/org/whispersystems/textsecuregcm/tests/storage/AccountsTest.java @@ -270,7 +270,7 @@ public class AccountsTest { private Device generateDevice(long id) { Random random = new Random(System.currentTimeMillis()); SignedPreKey signedPreKey = new SignedPreKey(random.nextInt(), "testPublicKey-" + random.nextInt(), "testSignature-" + random.nextInt()); - return new Device(id, "testName-" + random.nextInt(), "testAuthToken-" + random.nextInt(), "testSalt-" + random.nextInt(), null, "testGcmId-" + random.nextInt(), "testApnId-" + random.nextInt(), "testVoipApnId-" + random.nextInt(), random.nextBoolean(), random.nextInt(), signedPreKey, random.nextInt(), random.nextInt(), "testUserAgent-" + random.nextInt(), random.nextBoolean(), 0); + return new Device(id, "testName-" + random.nextInt(), "testAuthToken-" + random.nextInt(), "testSalt-" + random.nextInt(), null, "testGcmId-" + random.nextInt(), "testApnId-" + random.nextInt(), "testVoipApnId-" + random.nextInt(), random.nextBoolean(), random.nextInt(), signedPreKey, random.nextInt(), random.nextInt(), "testUserAgent-" + random.nextInt() , 0, new Device.DeviceCapabilities(random.nextBoolean())); } private Account generateAccount(String number, UUID uuid) {