public Account changeNumber(final Account account, final String number,
      @Nullable final IdentityKey pniIdentityKey,
      @Nullable final Map<Byte, ECSignedPreKey> deviceSignedPreKeys,
      @Nullable final Map<Byte, KEMSignedPreKey> devicePqLastResortPreKeys,
      @Nullable final List<IncomingMessage> deviceMessages,
      @Nullable final Map<Byte, Integer> pniRegistrationIds)
      throws InterruptedException, MismatchedDevicesException, StaleDevicesException {

    if (ObjectUtils.allNotNull(pniIdentityKey, deviceSignedPreKeys, deviceMessages, pniRegistrationIds)) {
      // AccountsManager validates the device set on deviceSignedPreKeys and pniRegistrationIds
      validateDeviceMessages(account, deviceMessages);
    } else if (!ObjectUtils.allNull(pniIdentityKey, deviceSignedPreKeys, deviceMessages, pniRegistrationIds)) {
      throw new IllegalArgumentException("PNI identity key, signed pre-keys, device messages, and registration IDs must be all null or all non-null");
    }

    if (number.equals(account.getNumber())) {
      // The client has gotten confused/desynchronized with us about their own phone number, most likely due to losing
      // our OK response to an immediately preceding change-number request, and are sending a change they don't realize
      // is a no-op change.
      //
      // We don't need to actually do a number-change operation in our DB, but we *do* need to accept their new key
      // material and distribute the sync messages, to be sure all clients agree with us and each other about what their
      // keys are. Pretend this change-number request was actually a PNI key distribution request.
      if (pniIdentityKey == null) {
        return account;
      }
      return updatePniKeys(account, pniIdentityKey, deviceSignedPreKeys, devicePqLastResortPreKeys, deviceMessages, pniRegistrationIds);
    }

    final Account updatedAccount = accountsManager.changeNumber(
        account, number, pniIdentityKey, deviceSignedPreKeys, devicePqLastResortPreKeys, pniRegistrationIds);

    if (deviceMessages != null) {
      sendDeviceMessages(updatedAccount, deviceMessages);
    }

    return updatedAccount;
  }
@Test
  void changeNumberMismatchedRegistrationId() {
    final Account account = mock(Account.class);
    when(account.getNumber()).thenReturn("+18005551234");

    final List<Device> devices = new ArrayList<>();

    for (byte i = 1; i <= 3; i++) {
      final Device device = mock(Device.class);
      when(device.getId()).thenReturn(i);
      when(device.getRegistrationId()).thenReturn((int) i);

      devices.add(device);
      when(account.getDevice(i)).thenReturn(Optional.of(device));
    }

    when(account.getDevices()).thenReturn(devices);

    final byte destinationDeviceId2 = 2;
    final byte destinationDeviceId3 = 3;
    final List<IncomingMessage> messages = List.of(
        new IncomingMessage(1, destinationDeviceId2, 1, "foo"),
        new IncomingMessage(1, destinationDeviceId3, 1, "foo"));

    final ECKeyPair pniIdentityKeyPair = Curve.generateKeyPair();
    final ECPublicKey pniIdentityKey = pniIdentityKeyPair.getPublicKey();

    final Map<Byte, ECSignedPreKey> preKeys = Map.of(Device.PRIMARY_ID,
        KeysHelper.signedECPreKey(1, pniIdentityKeyPair),
        destinationDeviceId2, KeysHelper.signedECPreKey(2, pniIdentityKeyPair),
        destinationDeviceId3, KeysHelper.signedECPreKey(3, pniIdentityKeyPair));
    final Map<Byte, Integer> registrationIds = Map.of(Device.PRIMARY_ID, 17, destinationDeviceId2, 47,
        destinationDeviceId3, 89);

    assertThrows(StaleDevicesException.class,
        () -> changeNumberManager.changeNumber(account, "+18005559876", new IdentityKey(Curve.generateKeyPair().getPublicKey()), preKeys, null, messages, registrationIds));
  }