Add a "change number" device/account capability

This commit is contained in:
Jon Chambers 2021-09-02 13:03:35 -04:00 committed by Jon Chambers
parent 18a6df34bd
commit 92f035bc2a
14 changed files with 121 additions and 28 deletions

View File

@ -228,6 +228,7 @@ public class DeviceController {
private boolean isCapabilityDowngrade(Account account, DeviceCapabilities capabilities, String userAgent) { private boolean isCapabilityDowngrade(Account account, DeviceCapabilities capabilities, String userAgent) {
boolean isDowngrade = false; boolean isDowngrade = false;
isDowngrade |= account.isChangeNumberSupported() && !capabilities.isChangeNumber();
isDowngrade |= account.isAnnouncementGroupSupported() && !capabilities.isAnnouncementGroup(); isDowngrade |= account.isAnnouncementGroupSupported() && !capabilities.isAnnouncementGroup();
isDowngrade |= account.isSenderKeySupported() && !capabilities.isSenderKey(); isDowngrade |= account.isSenderKeySupported() && !capabilities.isSenderKey();
isDowngrade |= account.isGv1MigrationSupported() && !capabilities.isGv1Migration(); isDowngrade |= account.isGv1MigrationSupported() && !capabilities.isGv1Migration();

View File

@ -15,7 +15,8 @@ public class UserCapabilities {
account.isGroupsV2Supported(), account.isGroupsV2Supported(),
account.isGv1MigrationSupported(), account.isGv1MigrationSupported(),
account.isSenderKeySupported(), account.isSenderKeySupported(),
account.isAnnouncementGroupSupported()); account.isAnnouncementGroupSupported(),
account.isChangeNumberSupported());
} }
@JsonProperty @JsonProperty
@ -30,13 +31,22 @@ public class UserCapabilities {
@JsonProperty @JsonProperty
private boolean announcementGroup; private boolean announcementGroup;
@JsonProperty
private boolean changeNumber;
public UserCapabilities() {} public UserCapabilities() {}
public UserCapabilities(boolean gv2, boolean gv1Migration, final boolean senderKey, final boolean announcementGroup) { public UserCapabilities(final boolean gv2,
boolean gv1Migration,
final boolean senderKey,
final boolean announcementGroup,
final boolean changeNumber) {
this.gv2 = gv2; this.gv2 = gv2;
this.gv1Migration = gv1Migration; this.gv1Migration = gv1Migration;
this.senderKey = senderKey; this.senderKey = senderKey;
this.announcementGroup = announcementGroup; this.announcementGroup = announcementGroup;
this.changeNumber = changeNumber;
} }
public boolean isGv2() { public boolean isGv2() {
@ -54,4 +64,8 @@ public class UserCapabilities {
public boolean isAnnouncementGroup() { public boolean isAnnouncementGroup() {
return announcementGroup; return announcementGroup;
} }
public boolean isChangeNumber() {
return changeNumber;
}
} }

View File

@ -180,6 +180,14 @@ public class Account {
.allMatch(device -> device.getCapabilities() != null && device.getCapabilities().isAnnouncementGroup()); .allMatch(device -> device.getCapabilities() != null && device.getCapabilities().isAnnouncementGroup());
} }
public boolean isChangeNumberSupported() {
requireNotStale();
return devices.stream()
.filter(Device::isEnabled)
.allMatch(device -> device.getCapabilities() != null && device.getCapabilities().isChangeNumber());
}
public boolean isEnabled() { public boolean isEnabled() {
requireNotStale(); requireNotStale();

View File

@ -279,10 +279,13 @@ public class Device {
@JsonProperty @JsonProperty
private boolean announcementGroup; private boolean announcementGroup;
@JsonProperty
private boolean changeNumber;
public DeviceCapabilities() {} public DeviceCapabilities() {}
public DeviceCapabilities(boolean gv2, final boolean gv2_2, final boolean gv2_3, boolean storage, boolean transfer, public DeviceCapabilities(boolean gv2, final boolean gv2_2, final boolean gv2_3, boolean storage, boolean transfer,
boolean gv1Migration, final boolean senderKey, final boolean announcementGroup) { boolean gv1Migration, final boolean senderKey, final boolean announcementGroup, final boolean changeNumber) {
this.gv2 = gv2; this.gv2 = gv2;
this.gv2_2 = gv2_2; this.gv2_2 = gv2_2;
this.gv2_3 = gv2_3; this.gv2_3 = gv2_3;
@ -291,6 +294,7 @@ public class Device {
this.gv1Migration = gv1Migration; this.gv1Migration = gv1Migration;
this.senderKey = senderKey; this.senderKey = senderKey;
this.announcementGroup = announcementGroup; this.announcementGroup = announcementGroup;
this.changeNumber = changeNumber;
} }
public boolean isGv2() { public boolean isGv2() {
@ -324,5 +328,9 @@ public class Device {
public boolean isAnnouncementGroup() { public boolean isAnnouncementGroup() {
return announcementGroup; return announcementGroup;
} }
public boolean isChangeNumber() {
return changeNumber;
}
} }
} }

View File

@ -511,7 +511,7 @@ class AccountsDynamoDbTest {
SignedPreKey signedPreKey = new SignedPreKey(random.nextInt(), "testPublicKey-" + random.nextInt(), "testSignature-" + random.nextInt()); 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(), return new Device(id, "testName-" + random.nextInt(), "testAuthToken-" + random.nextInt(), "testSalt-" + random.nextInt(),
"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(), random.nextBoolean(), random.nextBoolean(), random.nextBoolean(), random.nextBoolean(), random.nextBoolean(), "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(), random.nextBoolean(), random.nextBoolean(), random.nextBoolean(), random.nextBoolean(), random.nextBoolean(),
random.nextBoolean(), random.nextBoolean())); random.nextBoolean(), random.nextBoolean(), random.nextBoolean()));
} }
private Account generateAccount(String number, UUID uuid) { private Account generateAccount(String number, UUID uuid) {

View File

@ -192,7 +192,7 @@ class AccountsManagerConcurrentModificationIntegrationTest {
"testUserAgent-" + random.nextInt(), 0, "testUserAgent-" + random.nextInt(), 0,
new Device.DeviceCapabilities(random.nextBoolean(), random.nextBoolean(), random.nextBoolean(), new Device.DeviceCapabilities(random.nextBoolean(), random.nextBoolean(), random.nextBoolean(),
random.nextBoolean(), random.nextBoolean(), random.nextBoolean(), random.nextBoolean(), random.nextBoolean(), random.nextBoolean(),
random.nextBoolean(), random.nextBoolean()))); random.nextBoolean(), random.nextBoolean(), random.nextBoolean())));
}); });
uuid = account.getUuid(); uuid = account.getUuid();

View File

@ -70,7 +70,7 @@ public class DeviceTest {
@Parameters(method = "argumentsForTestIsGroupsV2Supported") @Parameters(method = "argumentsForTestIsGroupsV2Supported")
public void testIsGroupsV2Supported(final boolean master, final String apnId, final boolean gv2Capability, final boolean gv2_2Capability, final boolean gv2_3Capability, final boolean expectGv2Supported) { public void testIsGroupsV2Supported(final boolean master, final String apnId, final boolean gv2Capability, final boolean gv2_2Capability, final boolean gv2_3Capability, final boolean expectGv2Supported) {
final Device.DeviceCapabilities capabilities = new Device.DeviceCapabilities(gv2Capability, gv2_2Capability, gv2_3Capability, false, false, false, final Device.DeviceCapabilities capabilities = new Device.DeviceCapabilities(gv2Capability, gv2_2Capability, gv2_3Capability, false, false, false,
false, false); false, false, false);
final Device device = new Device(master ? 1 : 2, "test", "auth-token", "salt", final Device device = new Device(master ? 1 : 2, "test", "auth-token", "salt",
null, apnId, null, false, 1, null, 0, 0, "user-agent", 0, capabilities); null, apnId, null, false, 1, null, 0, 0, "user-agent", 0, capabilities);

View File

@ -121,6 +121,7 @@ class DeviceControllerTest {
when(account.isGv1MigrationSupported()).thenReturn(true); when(account.isGv1MigrationSupported()).thenReturn(true);
when(account.isSenderKeySupported()).thenReturn(true); when(account.isSenderKeySupported()).thenReturn(true);
when(account.isAnnouncementGroupSupported()).thenReturn(true); when(account.isAnnouncementGroupSupported()).thenReturn(true);
when(account.isChangeNumberSupported()).thenReturn(true);
when(pendingDevicesManager.getCodeForNumber(AuthHelper.VALID_NUMBER)).thenReturn( when(pendingDevicesManager.getCodeForNumber(AuthHelper.VALID_NUMBER)).thenReturn(
Optional.of(new StoredVerificationCode("5678901", System.currentTimeMillis(), null, null))); Optional.of(new StoredVerificationCode("5678901", System.currentTimeMillis(), null, null)));
@ -259,7 +260,7 @@ class DeviceControllerTest {
@ParameterizedTest @ParameterizedTest
@MethodSource @MethodSource
void deviceDowngradeCapabilitiesTest(final String userAgent, final boolean gv2, final boolean gv2_2, final boolean gv2_3, final int expectedStatus) { void deviceDowngradeCapabilitiesTest(final String userAgent, final boolean gv2, final boolean gv2_2, final boolean gv2_3, final int expectedStatus) {
DeviceCapabilities deviceCapabilities = new DeviceCapabilities(gv2, gv2_2, gv2_3, true, false, true, true, true); DeviceCapabilities deviceCapabilities = new DeviceCapabilities(gv2, gv2_2, gv2_3, true, false, true, true, true, true);
AccountAttributes accountAttributes = new AccountAttributes(false, 1234, null, null, true, deviceCapabilities); AccountAttributes accountAttributes = new AccountAttributes(false, 1234, null, null, true, deviceCapabilities);
Response response = resources.getJerseyTest() Response response = resources.getJerseyTest()
.target("/v1/devices/5678901") .target("/v1/devices/5678901")
@ -299,7 +300,7 @@ class DeviceControllerTest {
@Test @Test
void deviceDowngradeGv1MigrationTest() { void deviceDowngradeGv1MigrationTest() {
DeviceCapabilities deviceCapabilities = new DeviceCapabilities(true, true, true, true, false, false, true, true); DeviceCapabilities deviceCapabilities = new DeviceCapabilities(true, true, true, true, false, false, true, true, true);
AccountAttributes accountAttributes = new AccountAttributes(false, 1234, null, null, true, deviceCapabilities); AccountAttributes accountAttributes = new AccountAttributes(false, 1234, null, null, true, deviceCapabilities);
Response response = resources.getJerseyTest() Response response = resources.getJerseyTest()
.target("/v1/devices/5678901") .target("/v1/devices/5678901")
@ -310,7 +311,7 @@ class DeviceControllerTest {
assertThat(response.getStatus()).isEqualTo(409); assertThat(response.getStatus()).isEqualTo(409);
deviceCapabilities = new DeviceCapabilities(true, true, true, true, false, true, true, true); deviceCapabilities = new DeviceCapabilities(true, true, true, true, false, true, true, true, true);
accountAttributes = new AccountAttributes(false, 1234, null, null, true, deviceCapabilities); accountAttributes = new AccountAttributes(false, 1234, null, null, true, deviceCapabilities);
response = resources.getJerseyTest() response = resources.getJerseyTest()
.target("/v1/devices/5678901") .target("/v1/devices/5678901")
@ -324,7 +325,7 @@ class DeviceControllerTest {
@Test @Test
void deviceDowngradeSenderKeyTest() { void deviceDowngradeSenderKeyTest() {
DeviceCapabilities deviceCapabilities = new DeviceCapabilities(true, true, true, true, true, true, false, true); DeviceCapabilities deviceCapabilities = new DeviceCapabilities(true, true, true, true, true, true, false, true, true);
AccountAttributes accountAttributes = AccountAttributes accountAttributes =
new AccountAttributes(false, 1234, null, null, true, deviceCapabilities); new AccountAttributes(false, 1234, null, null, true, deviceCapabilities);
Response response = resources Response response = resources
@ -336,7 +337,7 @@ class DeviceControllerTest {
.put(Entity.entity(accountAttributes, MediaType.APPLICATION_JSON_TYPE)); .put(Entity.entity(accountAttributes, MediaType.APPLICATION_JSON_TYPE));
assertThat(response.getStatus()).isEqualTo(409); assertThat(response.getStatus()).isEqualTo(409);
deviceCapabilities = new DeviceCapabilities(true, true, true, true, true, true, true, true); deviceCapabilities = new DeviceCapabilities(true, true, true, true, true, true, true, true, true);
accountAttributes = new AccountAttributes(false, 1234, null, null, true, deviceCapabilities); accountAttributes = new AccountAttributes(false, 1234, null, null, true, deviceCapabilities);
response = resources response = resources
.getJerseyTest() .getJerseyTest()
@ -350,7 +351,7 @@ class DeviceControllerTest {
@Test @Test
void deviceDowngradeAnnouncementGroupTest() { void deviceDowngradeAnnouncementGroupTest() {
DeviceCapabilities deviceCapabilities = new DeviceCapabilities(true, true, true, true, true, true, true, false); DeviceCapabilities deviceCapabilities = new DeviceCapabilities(true, true, true, true, true, true, true, false, true);
AccountAttributes accountAttributes = AccountAttributes accountAttributes =
new AccountAttributes(false, 1234, null, null, true, deviceCapabilities); new AccountAttributes(false, 1234, null, null, true, deviceCapabilities);
Response response = resources Response response = resources
@ -362,7 +363,33 @@ class DeviceControllerTest {
.put(Entity.entity(accountAttributes, MediaType.APPLICATION_JSON_TYPE)); .put(Entity.entity(accountAttributes, MediaType.APPLICATION_JSON_TYPE));
assertThat(response.getStatus()).isEqualTo(409); assertThat(response.getStatus()).isEqualTo(409);
deviceCapabilities = new DeviceCapabilities(true, true, true, true, true, true, true, true); deviceCapabilities = new DeviceCapabilities(true, true, true, true, true, true, true, true, true);
accountAttributes = new AccountAttributes(false, 1234, null, null, true, deviceCapabilities);
response = resources
.getJerseyTest()
.target("/v1/devices/5678901")
.request()
.header("Authorization", AuthHelper.getProvisioningAuthHeader(AuthHelper.VALID_NUMBER, AuthHelper.VALID_PASSWORD))
.header("User-Agent", "Signal-Android/5.42.8675309 Android/30")
.put(Entity.entity(accountAttributes, MediaType.APPLICATION_JSON_TYPE));
assertThat(response.getStatus()).isEqualTo(200);
}
@Test
void deviceDowngradeChangeNumberTest() {
DeviceCapabilities deviceCapabilities = new DeviceCapabilities(true, true, true, true, true, true, true, true, false);
AccountAttributes accountAttributes =
new AccountAttributes(false, 1234, null, null, true, deviceCapabilities);
Response response = resources
.getJerseyTest()
.target("/v1/devices/5678901")
.request()
.header("Authorization", AuthHelper.getProvisioningAuthHeader(AuthHelper.VALID_NUMBER, AuthHelper.VALID_PASSWORD))
.header("User-Agent", "Signal-Android/5.42.8675309 Android/30")
.put(Entity.entity(accountAttributes, MediaType.APPLICATION_JSON_TYPE));
assertThat(response.getStatus()).isEqualTo(409);
deviceCapabilities = new DeviceCapabilities(true, true, true, true, true, true, true, true, true);
accountAttributes = new AccountAttributes(false, 1234, null, null, true, deviceCapabilities); accountAttributes = new AccountAttributes(false, 1234, null, null, true, deviceCapabilities);
response = resources response = resources
.getJerseyTest() .getJerseyTest()

View File

@ -158,22 +158,22 @@ class MessageControllerTest {
add(new Device(1, null, "foo", "bar", add(new Device(1, null, "foo", "bar",
"isgcm", null, null, false, 111, new SignedPreKey(333, "baz", "boop"), System.currentTimeMillis(), "isgcm", null, null, false, 111, new SignedPreKey(333, "baz", "boop"), System.currentTimeMillis(),
System.currentTimeMillis(), "Test", 0, new Device.DeviceCapabilities(true, false, false, true, true, false, System.currentTimeMillis(), "Test", 0, new Device.DeviceCapabilities(true, false, false, true, true, false,
false, false))); false, false, false)));
}}; }};
Set<Device> multiDeviceList = new HashSet<>() {{ Set<Device> multiDeviceList = new HashSet<>() {{
add(new Device(1, null, "foo", "bar", add(new Device(1, null, "foo", "bar",
"isgcm", null, null, false, 222, new SignedPreKey(111, "foo", "bar"), System.currentTimeMillis(), "isgcm", null, null, false, 222, new SignedPreKey(111, "foo", "bar"), System.currentTimeMillis(),
System.currentTimeMillis(), "Test", 0, new Device.DeviceCapabilities(true, false, false, true, false, false, System.currentTimeMillis(), "Test", 0, new Device.DeviceCapabilities(true, false, false, true, false, false,
false, false))); false, false, false)));
add(new Device(2, null, "foo", "bar", add(new Device(2, null, "foo", "bar",
"isgcm", null, null, false, 333, new SignedPreKey(222, "oof", "rab"), System.currentTimeMillis(), "isgcm", null, null, false, 333, new SignedPreKey(222, "oof", "rab"), System.currentTimeMillis(),
System.currentTimeMillis(), "Test", 0, new Device.DeviceCapabilities(true, false, false, true, false, false, System.currentTimeMillis(), "Test", 0, new Device.DeviceCapabilities(true, false, false, true, false, false,
false, false))); false, false, false)));
add(new Device(3, null, "foo", "bar", add(new Device(3, null, "foo", "bar",
"isgcm", null, null, false, 444, null, System.currentTimeMillis() - TimeUnit.DAYS.toMillis(31), "isgcm", null, null, false, 444, null, System.currentTimeMillis() - TimeUnit.DAYS.toMillis(31),
System.currentTimeMillis(), "Test", 0, new Device.DeviceCapabilities(false, false, false, false, false, false, System.currentTimeMillis(), "Test", 0, new Device.DeviceCapabilities(false, false, false, false, false, false,
false, false))); false, false, false)));
}}; }};
Account singleDeviceAccount = new Account(SINGLE_DEVICE_RECIPIENT, SINGLE_DEVICE_UUID, singleDeviceList, "1234".getBytes()); Account singleDeviceAccount = new Account(SINGLE_DEVICE_RECIPIENT, SINGLE_DEVICE_UUID, singleDeviceList, "1234".getBytes());

View File

@ -130,6 +130,7 @@ class ProfileControllerTest {
when(profileAccount.isGv1MigrationSupported()).thenReturn(false); when(profileAccount.isGv1MigrationSupported()).thenReturn(false);
when(profileAccount.isSenderKeySupported()).thenReturn(false); when(profileAccount.isSenderKeySupported()).thenReturn(false);
when(profileAccount.isAnnouncementGroupSupported()).thenReturn(false); when(profileAccount.isAnnouncementGroupSupported()).thenReturn(false);
when(profileAccount.isChangeNumberSupported()).thenReturn(false);
when(profileAccount.getCurrentProfileVersion()).thenReturn(Optional.empty()); when(profileAccount.getCurrentProfileVersion()).thenReturn(Optional.empty());
Account capabilitiesAccount = mock(Account.class); Account capabilitiesAccount = mock(Account.class);
@ -142,6 +143,7 @@ class ProfileControllerTest {
when(capabilitiesAccount.isGv1MigrationSupported()).thenReturn(true); when(capabilitiesAccount.isGv1MigrationSupported()).thenReturn(true);
when(capabilitiesAccount.isSenderKeySupported()).thenReturn(true); when(capabilitiesAccount.isSenderKeySupported()).thenReturn(true);
when(capabilitiesAccount.isAnnouncementGroupSupported()).thenReturn(true); when(capabilitiesAccount.isAnnouncementGroupSupported()).thenReturn(true);
when(capabilitiesAccount.isChangeNumberSupported()).thenReturn(true);
when(accountsManager.get(AuthHelper.VALID_NUMBER_TWO)).thenReturn(Optional.of(profileAccount)); when(accountsManager.get(AuthHelper.VALID_NUMBER_TWO)).thenReturn(Optional.of(profileAccount));
when(accountsManager.get(AuthHelper.VALID_UUID_TWO)).thenReturn(Optional.of(profileAccount)); when(accountsManager.get(AuthHelper.VALID_UUID_TWO)).thenReturn(Optional.of(profileAccount));

View File

@ -49,6 +49,10 @@ class AccountTest {
private final Device announcementGroupIncapableDevice = mock(Device.class); private final Device announcementGroupIncapableDevice = mock(Device.class);
private final Device announcementGroupIncapableExpiredDevice = mock(Device.class); private final Device announcementGroupIncapableExpiredDevice = mock(Device.class);
private final Device changeNumberCapableDevice = mock(Device.class);
private final Device changeNumberIncapableDevice = mock(Device.class);
private final Device changeNumberIncapableExpiredDevice = mock(Device.class);
@BeforeEach @BeforeEach
void setup() { void setup() {
when(oldMasterDevice.getLastSeen()).thenReturn(System.currentTimeMillis() - TimeUnit.DAYS.toMillis(366)); when(oldMasterDevice.getLastSeen()).thenReturn(System.currentTimeMillis() - TimeUnit.DAYS.toMillis(366));
@ -84,40 +88,52 @@ class AccountTest {
when(gv2IncapableExpiredDevice.isEnabled()).thenReturn(false); when(gv2IncapableExpiredDevice.isEnabled()).thenReturn(false);
when(gv1MigrationCapableDevice.getCapabilities()).thenReturn( when(gv1MigrationCapableDevice.getCapabilities()).thenReturn(
new DeviceCapabilities(true, true, true, true, true, true, false, false)); new DeviceCapabilities(true, true, true, true, true, true, false, false, false));
when(gv1MigrationCapableDevice.isEnabled()).thenReturn(true); when(gv1MigrationCapableDevice.isEnabled()).thenReturn(true);
when(gv1MigrationIncapableDevice.getCapabilities()).thenReturn( when(gv1MigrationIncapableDevice.getCapabilities()).thenReturn(
new DeviceCapabilities(true, true, true, true, true, false, false, false)); new DeviceCapabilities(true, true, true, true, true, false, false, false, false));
when(gv1MigrationIncapableDevice.isEnabled()).thenReturn(true); when(gv1MigrationIncapableDevice.isEnabled()).thenReturn(true);
when(gv1MigrationIncapableExpiredDevice.getCapabilities()).thenReturn( when(gv1MigrationIncapableExpiredDevice.getCapabilities()).thenReturn(
new DeviceCapabilities(true, true, true, true, true, false, false, false)); new DeviceCapabilities(true, true, true, true, true, false, false, false, false));
when(gv1MigrationIncapableExpiredDevice.isEnabled()).thenReturn(false); when(gv1MigrationIncapableExpiredDevice.isEnabled()).thenReturn(false);
when(senderKeyCapableDevice.getCapabilities()).thenReturn( when(senderKeyCapableDevice.getCapabilities()).thenReturn(
new DeviceCapabilities(true, true, true, true, true, true, true, false)); new DeviceCapabilities(true, true, true, true, true, true, true, false, false));
when(senderKeyCapableDevice.isEnabled()).thenReturn(true); when(senderKeyCapableDevice.isEnabled()).thenReturn(true);
when(senderKeyIncapableDevice.getCapabilities()).thenReturn( when(senderKeyIncapableDevice.getCapabilities()).thenReturn(
new DeviceCapabilities(true, true, true, true, true, true, false, false)); new DeviceCapabilities(true, true, true, true, true, true, false, false, false));
when(senderKeyIncapableDevice.isEnabled()).thenReturn(true); when(senderKeyIncapableDevice.isEnabled()).thenReturn(true);
when(senderKeyIncapableExpiredDevice.getCapabilities()).thenReturn( when(senderKeyIncapableExpiredDevice.getCapabilities()).thenReturn(
new DeviceCapabilities(true, true, true, true, true, true, false, false)); new DeviceCapabilities(true, true, true, true, true, true, false, false, false));
when(senderKeyIncapableExpiredDevice.isEnabled()).thenReturn(false); when(senderKeyIncapableExpiredDevice.isEnabled()).thenReturn(false);
when(announcementGroupCapableDevice.getCapabilities()).thenReturn( when(announcementGroupCapableDevice.getCapabilities()).thenReturn(
new DeviceCapabilities(true, true, true, true, true, true, true, true)); new DeviceCapabilities(true, true, true, true, true, true, true, true, false));
when(announcementGroupCapableDevice.isEnabled()).thenReturn(true); when(announcementGroupCapableDevice.isEnabled()).thenReturn(true);
when(announcementGroupIncapableDevice.getCapabilities()).thenReturn( when(announcementGroupIncapableDevice.getCapabilities()).thenReturn(
new DeviceCapabilities(true, true, true, true, true, true, true, false)); new DeviceCapabilities(true, true, true, true, true, true, true, false, false));
when(announcementGroupIncapableDevice.isEnabled()).thenReturn(true); when(announcementGroupIncapableDevice.isEnabled()).thenReturn(true);
when(announcementGroupIncapableExpiredDevice.getCapabilities()).thenReturn( when(announcementGroupIncapableExpiredDevice.getCapabilities()).thenReturn(
new DeviceCapabilities(true, true, true, true, true, true, true, false)); new DeviceCapabilities(true, true, true, true, true, true, true, false, false));
when(announcementGroupIncapableExpiredDevice.isEnabled()).thenReturn(false); when(announcementGroupIncapableExpiredDevice.isEnabled()).thenReturn(false);
when(changeNumberCapableDevice.getCapabilities()).thenReturn(
new DeviceCapabilities(true, true, true, true, true, true, true, false, true));
when(changeNumberCapableDevice.isEnabled()).thenReturn(true);
when(changeNumberIncapableDevice.getCapabilities()).thenReturn(
new DeviceCapabilities(true, true, true, true, true, true, true, false, false));
when(changeNumberIncapableDevice.isEnabled()).thenReturn(true);
when(changeNumberIncapableExpiredDevice.getCapabilities()).thenReturn(
new DeviceCapabilities(true, true, true, true, true, true, true, false, false));
when(changeNumberIncapableExpiredDevice.isEnabled()).thenReturn(false);
} }
@Test @Test
@ -275,6 +291,19 @@ class AccountTest {
"1234".getBytes(StandardCharsets.UTF_8)).isAnnouncementGroupSupported()).isTrue(); "1234".getBytes(StandardCharsets.UTF_8)).isAnnouncementGroupSupported()).isTrue();
} }
@Test
void isChangeNumberSupported() {
assertThat(new Account("+18005551234", UUID.randomUUID(),
Set.of(changeNumberCapableDevice),
"1234".getBytes(StandardCharsets.UTF_8)).isChangeNumberSupported()).isTrue();
assertThat(new Account("+18005551234", UUID.randomUUID(),
Set.of(changeNumberCapableDevice, changeNumberIncapableDevice),
"1234".getBytes(StandardCharsets.UTF_8)).isChangeNumberSupported()).isFalse();
assertThat(new Account("+18005551234", UUID.randomUUID(),
Set.of(changeNumberCapableDevice, changeNumberIncapableExpiredDevice),
"1234".getBytes(StandardCharsets.UTF_8)).isChangeNumberSupported()).isTrue();
}
@Test @Test
void stale() { void stale() {
final Account account = new Account("+14151234567", UUID.randomUUID(), Collections.emptySet(), new byte[0]); final Account account = new Account("+14151234567", UUID.randomUUID(), Collections.emptySet(), new byte[0]);

View File

@ -606,7 +606,7 @@ class AccountsManagerTest {
@ValueSource(booleans = {true, false}) @ValueSource(booleans = {true, false})
void testCreateWithStorageCapability(final boolean hasStorage) throws InterruptedException { void testCreateWithStorageCapability(final boolean hasStorage) throws InterruptedException {
final AccountAttributes attributes = new AccountAttributes(false, 0, null, null, true, final AccountAttributes attributes = new AccountAttributes(false, 0, null, null, true,
new DeviceCapabilities(false, false, false, hasStorage, false, false, false, false)); new DeviceCapabilities(false, false, false, hasStorage, false, false, false, false, false));
final Account account = accountsManager.create("+18005550123", "password", null, attributes); final Account account = accountsManager.create("+18005550123", "password", null, attributes);

View File

@ -325,7 +325,7 @@ public class AccountsTest {
SignedPreKey signedPreKey = new SignedPreKey(random.nextInt(), "testPublicKey-" + random.nextInt(), "testSignature-" + random.nextInt()); 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(), return new Device(id, "testName-" + random.nextInt(), "testAuthToken-" + random.nextInt(), "testSalt-" + random.nextInt(),
"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(), random.nextBoolean(), random.nextBoolean(), random.nextBoolean(), random.nextBoolean(), random.nextBoolean(), "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(), random.nextBoolean(), random.nextBoolean(), random.nextBoolean(), random.nextBoolean(), random.nextBoolean(),
random.nextBoolean(), random.nextBoolean())); random.nextBoolean(), random.nextBoolean(), random.nextBoolean()));
} }
private Account generateAccount(String number, UUID uuid) { private Account generateAccount(String number, UUID uuid) {

View File

@ -141,6 +141,10 @@ public class AccountsHelper {
when(updatedAccount.isAnnouncementGroupSupported()).thenAnswer(stubbing); when(updatedAccount.isAnnouncementGroupSupported()).thenAnswer(stubbing);
break; break;
} }
case "isChangeNumberSupported": {
when(updatedAccount.isChangeNumberSupported()).thenAnswer(stubbing);
break;
}
case "getEnabledDeviceCount": { case "getEnabledDeviceCount": {
when(updatedAccount.getEnabledDeviceCount()).thenAnswer(stubbing); when(updatedAccount.getEnabledDeviceCount()).thenAnswer(stubbing);
break; break;