Implement new updateDevice command to update device names

Fixes #1906
This commit is contained in:
AsamK 2026-01-23 19:59:59 +01:00
parent a9be1aa608
commit 8eb9662694
10 changed files with 133 additions and 1 deletions

View File

@ -2426,6 +2426,13 @@
"queryAllDeclaredConstructors":true,
"methods":[{"name":"<init>","parameterTypes":["java.lang.String","java.lang.String"] }, {"name":"<init>","parameterTypes":["java.lang.String","java.lang.String","int","kotlin.jvm.internal.DefaultConstructorMarker"] }]
},
{
"name":"org.whispersystems.signalservice.api.link.SetDeviceNameRequest",
"allDeclaredFields":true,
"queryAllDeclaredMethods":true,
"queryAllDeclaredConstructors":true,
"methods":[{"name":"<init>","parameterTypes":["java.lang.String"] }, {"name":"getDeviceName","parameterTypes":[] }]
},
{
"name":"org.whispersystems.signalservice.api.messages.calls.HangupMessage",
"allDeclaredFields":true,
@ -3136,7 +3143,6 @@
{
"name":"org.whispersystems.signalservice.internal.storage.protos.GroupV2Record",
"allDeclaredFields":true,
"fields":[{"name":"archived"}, {"name":"avatarColor"}, {"name":"blocked"}, {"name":"dontNotifyForMentionsIfMuted"}, {"name":"hideStory"}, {"name":"markedUnread"}, {"name":"masterKey"}, {"name":"mutedUntilTimestamp"}, {"name":"storySendMode"}, {"name":"whitelisted"}],
"methods":[{"name":"adapter","parameterTypes":[] }, {"name":"unknownFields","parameterTypes":[] }]
},
{

View File

@ -154,6 +154,8 @@ public interface Manager extends Closeable {
List<Device> getLinkedDevices() throws IOException;
void updateLinkedDevice(int deviceId, String name) throws IOException, NotPrimaryDeviceException;
void removeLinkedDevices(int deviceId) throws IOException, NotPrimaryDeviceException;
void addDeviceLink(DeviceLinkUrl linkUri) throws IOException, InvalidDeviceLinkException, NotPrimaryDeviceException, DeviceLimitExceededException;

View File

@ -0,0 +1,20 @@
package org.asamk.signal.manager.actions;
import org.asamk.signal.manager.helper.Context;
public class RetrieveDeviceNameAction implements HandleAction {
private static final RetrieveDeviceNameAction INSTANCE = new RetrieveDeviceNameAction();
public static RetrieveDeviceNameAction create() {
return INSTANCE;
}
private RetrieveDeviceNameAction() {
}
@Override
public void execute(Context context) throws Throwable {
context.getAccountHelper().refreshDeviceName();
}
}

View File

@ -522,6 +522,22 @@ public class AccountHelper {
account.setEncryptedDeviceName(encryptedDeviceName);
}
public void setDeviceName(int deviceId, String deviceName) throws IOException {
final var privateKey = account.getAciIdentityKeyPair().getPrivateKey();
final var encryptedDeviceName = DeviceNameUtil.encryptDeviceName(deviceName, privateKey);
handleResponseException(dependencies.getLinkDeviceApi().setDeviceName(encryptedDeviceName, deviceId));
context.getSyncHelper().sendDeviceNameChange(deviceId);
}
public void refreshDeviceName() throws IOException {
final var devices = handleResponseException(dependencies.getLinkDeviceApi().getDevices());
final var deviceId = account.getDeviceId();
final var device = devices.stream().filter(d -> d.id == deviceId).findFirst();
if (device.isPresent()) {
account.setEncryptedDeviceName(device.get().name);
}
}
public void updateAccountAttributes() throws IOException {
handleResponseException(dependencies.getAccountApi().setAccountAttributes(account.getAccountAttributes(null)));
}

View File

@ -5,6 +5,7 @@ import org.asamk.signal.manager.actions.HandleAction;
import org.asamk.signal.manager.actions.RefreshPreKeysAction;
import org.asamk.signal.manager.actions.RenewSessionAction;
import org.asamk.signal.manager.actions.ResendMessageAction;
import org.asamk.signal.manager.actions.RetrieveDeviceNameAction;
import org.asamk.signal.manager.actions.RetrieveProfileAction;
import org.asamk.signal.manager.actions.SendGroupInfoAction;
import org.asamk.signal.manager.actions.SendGroupInfoRequestAction;
@ -633,6 +634,12 @@ public final class IncomingMessageHandler {
context.getAccountHelper().handlePniChangeNumberMessage(pniChangeNumber, updatedPni);
}
}
if (syncMessage.getDeviceNameChange().isPresent()) {
final var deviceNameChange = syncMessage.getDeviceNameChange().get();
if (deviceNameChange.deviceId != null && deviceNameChange.deviceId == account.getDeviceId()) {
actions.add(RetrieveDeviceNameAction.create());
}
}
return actions;
}

View File

@ -52,6 +52,8 @@ import java.util.Optional;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import okio.ByteString;
public class SyncHelper {
private static final Logger logger = LoggerFactory.getLogger(SyncHelper.class);
@ -406,6 +408,11 @@ public class SyncHelper {
return context.getSendHelper().sendSyncMessage(SignalServiceSyncMessage.forMessageRequestResponse(response));
}
public SendMessageResult sendDeviceNameChange(final int deviceId) {
final var deviceNameChange = new SyncMessage.DeviceNameChange(deviceId, ByteString.EMPTY);
return context.getSendHelper().sendSyncMessage(SignalServiceSyncMessage.forDeviceNameChange(deviceNameChange));
}
private SendMessageResult requestSyncData(final SyncMessage.Request.Type type) {
var r = new SyncMessage.Request.Builder().type(type).build();
var message = SignalServiceSyncMessage.forRequest(new RequestMessage(r));

View File

@ -487,6 +487,21 @@ public class ManagerImpl implements Manager {
}).toList();
}
@Override
public void updateLinkedDevice(
final int deviceId,
final String name
) throws IOException, NotPrimaryDeviceException {
if (deviceId == account.getDeviceId()) {
context.getAccountHelper().setDeviceName(name);
} else {
if (!account.isPrimaryDevice()) {
throw new NotPrimaryDeviceException();
}
context.getAccountHelper().setDeviceName(deviceId, name);
}
}
private Long getPlaintextCreatedAt(DeviceInfo d) {
final var DECRYPTION_INFO = "deviceCreatedAt";
var identityKey = account.getAciIdentityKeyPair().getPrivateKey();

View File

@ -58,6 +58,7 @@ public class Commands {
addCommand(new UpdateAccountCommand());
addCommand(new UpdateConfigurationCommand());
addCommand(new UpdateContactCommand());
addCommand(new UpdateDeviceCommand());
addCommand(new UpdateGroupCommand());
addCommand(new UpdateProfileCommand());
addCommand(new UploadStickerPackCommand());

View File

@ -0,0 +1,50 @@
package org.asamk.signal.commands;
import net.sourceforge.argparse4j.inf.Namespace;
import net.sourceforge.argparse4j.inf.Subparser;
import org.asamk.signal.commands.exceptions.CommandException;
import org.asamk.signal.commands.exceptions.IOErrorException;
import org.asamk.signal.commands.exceptions.UserErrorException;
import org.asamk.signal.manager.Manager;
import org.asamk.signal.manager.api.NotPrimaryDeviceException;
import org.asamk.signal.output.OutputWriter;
import java.io.IOException;
public class UpdateDeviceCommand implements JsonRpcLocalCommand {
@Override
public String getName() {
return "updateDevice";
}
@Override
public void attachToSubparser(final Subparser subparser) {
subparser.help("Update a linked device.");
subparser.addArgument("-d", "--device-id", "--deviceId")
.type(int.class)
.required(true)
.help("Specify the device you want to update. Use listDevices to see the deviceIds.");
subparser.addArgument("-n", "--device-name")
.required(true)
.help("Specify a name to describe the given device.");
}
@Override
public void handleCommand(
final Namespace ns,
final Manager m,
final OutputWriter outputWriter
) throws CommandException {
try {
final var deviceId = ns.getInt("device-id");
final var deviceName = ns.getString("device-name");
m.updateLinkedDevice(deviceId, deviceName);
} catch (NotPrimaryDeviceException e) {
throw new UserErrorException("This command doesn't work on linked devices.");
} catch (IOException e) {
throw new IOErrorException("Error while updating device: " + e.getMessage(), e);
}
}
}

View File

@ -246,6 +246,14 @@ public class DbusManagerImpl implements Manager {
}).toList();
}
@Override
public void updateLinkedDevice(
final int deviceId,
final String name
) throws IOException, NotPrimaryDeviceException {
throw new UnsupportedOperationException();
}
@Override
public void removeLinkedDevices(final int deviceId) throws IOException {
final var devicePath = signal.getDevice(deviceId);