]> nmode's Git Repositories - signal-cli/commitdiff
Refactor dbus linked devices interface
authorAsamK <asamk@gmx.de>
Sat, 2 Oct 2021 16:04:30 +0000 (18:04 +0200)
committerAsamK <asamk@gmx.de>
Sat, 2 Oct 2021 16:04:30 +0000 (18:04 +0200)
Export a separate dbus object for each device

lib/src/main/java/org/asamk/signal/manager/Manager.java
lib/src/main/java/org/asamk/signal/manager/ManagerImpl.java
src/main/java/org/asamk/Signal.java
src/main/java/org/asamk/signal/App.java
src/main/java/org/asamk/signal/commands/DaemonCommand.java
src/main/java/org/asamk/signal/commands/RemoveDeviceCommand.java
src/main/java/org/asamk/signal/dbus/DbusManagerImpl.java
src/main/java/org/asamk/signal/dbus/DbusSignalImpl.java

index cd7b033550f4ac998f8387bd2dfd25b5c1c98bb3..7a4219660dd959c6af3cc14af8e5fafe77b182e1 100644 (file)
@@ -117,7 +117,7 @@ public interface Manager extends Closeable {
 
     List<Device> getLinkedDevices() throws IOException;
 
-    void removeLinkedDevices(int deviceId) throws IOException;
+    void removeLinkedDevices(long deviceId) throws IOException;
 
     void addDeviceLink(URI linkUri) throws IOException, InvalidKeyException;
 
index 86ec34c1cbc7dcd61822d9e98371a5d39d70a864..6a039c69fba743227ee00744af8a0d9d31b6028c 100644 (file)
@@ -414,7 +414,7 @@ public class ManagerImpl implements Manager {
     }
 
     @Override
-    public void removeLinkedDevices(int deviceId) throws IOException {
+    public void removeLinkedDevices(long deviceId) throws IOException {
         dependencies.getAccountManager().removeDevice(deviceId);
         var devices = dependencies.getAccountManager().getDevices();
         account.setMultiDevice(devices.size() > 1);
index cc521f6d1eefbf763589ca6ac354edef18dde04b..b88000858c38e1b4b1b15c82e782ae2e49157263 100644 (file)
@@ -1,8 +1,11 @@
 package org.asamk;
 
+import org.freedesktop.dbus.DBusPath;
+import org.freedesktop.dbus.annotations.DBusProperty;
 import org.freedesktop.dbus.exceptions.DBusException;
 import org.freedesktop.dbus.exceptions.DBusExecutionException;
 import org.freedesktop.dbus.interfaces.DBusInterface;
+import org.freedesktop.dbus.interfaces.Properties;
 import org.freedesktop.dbus.messages.DBusSignal;
 
 import java.util.List;
@@ -97,11 +100,11 @@ public interface Signal extends DBusInterface {
 
     void addDevice(String uri) throws Error.InvalidUri;
 
-    void removeDevice(int deviceId) throws Error.Failure;
+    DBusPath getDevice(long deviceId);
 
-    List<String> listDevices() throws Error.Failure;
+    List<DBusPath> listDevices() throws Error.Failure;
 
-    void updateDeviceName(String deviceName) throws Error.Failure;
+    DBusPath getThisDevice();
 
     void updateProfile(
             String givenName,
@@ -255,6 +258,15 @@ public interface Signal extends DBusInterface {
         }
     }
 
+    @DBusProperty(name = "Id", type = Integer.class, access = DBusProperty.Access.READ)
+    @DBusProperty(name = "Name", type = String.class)
+    @DBusProperty(name = "Created", type = String.class, access = DBusProperty.Access.READ)
+    @DBusProperty(name = "LastSeen", type = String.class, access = DBusProperty.Access.READ)
+    interface Device extends DBusInterface, Properties {
+
+        void removeDevice() throws Error.Failure;
+    }
+
     interface Error {
 
         class AttachmentInvalid extends DBusExecutionException {
index c44c737c3385648dae21eb8d2c1a260670fcdc00..3d35ff8f120f73228900119b5672254a9e60ef11 100644 (file)
@@ -349,7 +349,7 @@ public class App {
             ((ExtendedDbusCommand) command).handleCommand(ns, ts, dBusConn, outputWriter);
         } else if (command instanceof LocalCommand) {
             try {
-                ((LocalCommand) command).handleCommand(ns, new DbusManagerImpl(ts), outputWriter);
+                ((LocalCommand) command).handleCommand(ns, new DbusManagerImpl(ts, dBusConn), outputWriter);
             } catch (UnsupportedOperationException e) {
                 throw new UserErrorException("Command is not yet implemented via dbus", e);
             } catch (DBusExecutionException e) {
index 5045db9a28c89fce326bdfe780c9c479ba3c7ea8..02063b8721fbc7bea4de1ceb08ca0c948f3ab329 100644 (file)
@@ -120,7 +120,10 @@ public class DaemonCommand implements MultiLocalCommand {
     private Thread run(
             DBusConnection conn, String objectPath, Manager m, OutputWriter outputWriter, boolean ignoreAttachments
     ) throws DBusException {
-        conn.exportObject(new DbusSignalImpl(m, objectPath));
+        final var signal = new DbusSignalImpl(m, conn, objectPath);
+        conn.exportObject(signal);
+        final var initThread = new Thread(signal::initObjects);
+        initThread.start();
 
         logger.info("Exported dbus object: " + objectPath);
 
@@ -136,6 +139,11 @@ public class DaemonCommand implements MultiLocalCommand {
                     logger.warn("Receiving messages failed, retrying", e);
                 }
             }
+            try {
+                initThread.join();
+            } catch (InterruptedException ignored) {
+            }
+            signal.close();
         });
 
         thread.start();
index d67cc5eafda8e126363268cb500ab1a177daddf5..4fcad79df52dcf5efffdf457da52e024cb3096a3 100644 (file)
@@ -21,7 +21,7 @@ public class RemoveDeviceCommand implements JsonRpcLocalCommand {
     public void attachToSubparser(final Subparser subparser) {
         subparser.help("Remove a linked device.");
         subparser.addArgument("-d", "--device-id", "--deviceId")
-                .type(int.class)
+                .type(long.class)
                 .required(true)
                 .help("Specify the device you want to remove. Use listDevices to see the deviceIds.");
     }
@@ -31,7 +31,7 @@ public class RemoveDeviceCommand implements JsonRpcLocalCommand {
             final Namespace ns, final Manager m, final OutputWriter outputWriter
     ) throws CommandException {
         try {
-            int deviceId = ns.getInt("device-id");
+            final var deviceId = ns.getLong("device-id");
             m.removeLinkedDevices(deviceId);
         } catch (IOException e) {
             throw new IOErrorException("Error while removing device: " + e.getMessage(), e);
index ea776797b7182c3c841804e734667ba367cfd6b5..3124a5b05ed8ee655418a466bab56c44c5edc83d 100644 (file)
@@ -1,6 +1,7 @@
 package org.asamk.signal.dbus;
 
 import org.asamk.Signal;
+import org.asamk.signal.DbusConfig;
 import org.asamk.signal.manager.AttachmentInvalidException;
 import org.asamk.signal.manager.Manager;
 import org.asamk.signal.manager.NotMasterDeviceException;
@@ -25,6 +26,10 @@ import org.asamk.signal.manager.groups.NotAGroupMemberException;
 import org.asamk.signal.manager.storage.recipients.Contact;
 import org.asamk.signal.manager.storage.recipients.Profile;
 import org.asamk.signal.manager.storage.recipients.RecipientAddress;
+import org.freedesktop.dbus.DBusPath;
+import org.freedesktop.dbus.connections.impl.DBusConnection;
+import org.freedesktop.dbus.exceptions.DBusException;
+import org.freedesktop.dbus.interfaces.DBusInterface;
 import org.whispersystems.libsignal.IdentityKey;
 import org.whispersystems.libsignal.InvalidKeyException;
 import org.whispersystems.libsignal.util.Pair;
@@ -58,9 +63,11 @@ import java.util.stream.Collectors;
 public class DbusManagerImpl implements Manager {
 
     private final Signal signal;
+    private final DBusConnection connection;
 
-    public DbusManagerImpl(final Signal signal) {
+    public DbusManagerImpl(final Signal signal, DBusConnection connection) {
         this.signal = signal;
+        this.connection = connection;
     }
 
     @Override
@@ -89,7 +96,8 @@ public class DbusManagerImpl implements Manager {
     @Override
     public void updateAccountAttributes(final String deviceName) throws IOException {
         if (deviceName != null) {
-            signal.updateDeviceName(deviceName);
+            final var devicePath = signal.getThisDevice();
+            getRemoteObject(devicePath, Signal.Device.class).Set("org.asamk.Signal.Device", "Name", deviceName);
         }
     }
 
@@ -136,15 +144,21 @@ public class DbusManagerImpl implements Manager {
 
     @Override
     public List<Device> getLinkedDevices() throws IOException {
-        return signal.listDevices()
-                .stream()
-                .map(name -> new Device(-1, name, 0, 0, false))
-                .collect(Collectors.toList());
+        final var thisDevice = signal.getThisDevice();
+        return signal.listDevices().stream().map(devicePath -> {
+            final var device = getRemoteObject(devicePath, Signal.Device.class).GetAll("org.asamk.Signal.Device");
+            return new Device((long) device.get("Id").getValue(),
+                    (String) device.get("Name").getValue(),
+                    (long) device.get("Created").getValue(),
+                    (long) device.get("LastSeen").getValue(),
+                    thisDevice.equals(devicePath));
+        }).collect(Collectors.toList());
     }
 
     @Override
-    public void removeLinkedDevices(final int deviceId) throws IOException {
-        signal.removeDevice(deviceId);
+    public void removeLinkedDevices(final long deviceId) throws IOException {
+        final var devicePath = signal.getDevice(deviceId);
+        getRemoteObject(devicePath, Signal.Device.class).removeDevice();
     }
 
     @Override
@@ -494,4 +508,12 @@ public class DbusManagerImpl implements Manager {
     private String emptyIfNull(final String string) {
         return string == null ? "" : string;
     }
+
+    private <T extends DBusInterface> T getRemoteObject(final DBusPath devicePath, final Class<T> type) {
+        try {
+            return connection.getRemoteObject(DbusConfig.getBusname(), devicePath.getPath(), type);
+        } catch (DBusException e) {
+            throw new AssertionError(e);
+        }
+    }
 }
index 1a4fdc10240fb9394aa1f5c15582cd5fc9cb74ba..ab9c89b2ee6afd3f087356d7bfcbece2a5b60bd5 100644 (file)
@@ -7,7 +7,6 @@ import org.asamk.signal.manager.Manager;
 import org.asamk.signal.manager.NotMasterDeviceException;
 import org.asamk.signal.manager.StickerPackInvalidException;
 import org.asamk.signal.manager.UntrustedIdentityException;
-import org.asamk.signal.manager.api.Device;
 import org.asamk.signal.manager.api.Identity;
 import org.asamk.signal.manager.api.Message;
 import org.asamk.signal.manager.api.RecipientIdentifier;
@@ -21,6 +20,9 @@ import org.asamk.signal.manager.groups.NotAGroupMemberException;
 import org.asamk.signal.manager.storage.recipients.Profile;
 import org.asamk.signal.manager.storage.recipients.RecipientAddress;
 import org.asamk.signal.util.ErrorUtils;
+import org.freedesktop.dbus.DBusPath;
+import org.freedesktop.dbus.connections.impl.DBusConnection;
+import org.freedesktop.dbus.exceptions.DBusException;
 import org.freedesktop.dbus.exceptions.DBusExecutionException;
 import org.whispersystems.libsignal.InvalidKeyException;
 import org.whispersystems.libsignal.util.Pair;
@@ -49,16 +51,24 @@ import java.util.stream.Stream;
 public class DbusSignalImpl implements Signal {
 
     private final Manager m;
+    private final DBusConnection connection;
     private final String objectPath;
 
-    public DbusSignalImpl(final Manager m, final String objectPath) {
+    private DBusPath thisDevice;
+    private final List<DBusPath> devices = new ArrayList<>();
+
+    public DbusSignalImpl(final Manager m, DBusConnection connection, final String objectPath) {
         this.m = m;
+        this.connection = connection;
         this.objectPath = objectPath;
     }
 
-    @Override
-    public boolean isRemote() {
-        return false;
+    public void initObjects() {
+        updateDevices();
+    }
+
+    public void close() {
+        unExportDevices();
     }
 
     @Override
@@ -85,33 +95,51 @@ public class DbusSignalImpl implements Signal {
     }
 
     @Override
-    public void removeDevice(int deviceId) {
-        try {
-            m.removeLinkedDevices(deviceId);
-        } catch (IOException e) {
-            throw new Error.Failure(e.getClass().getSimpleName() + ": Error while removing device: " + e.getMessage());
-        }
+    public DBusPath getDevice(long deviceId) {
+        updateDevices();
+        return new DBusPath(getDeviceObjectPath(objectPath, deviceId));
     }
 
     @Override
-    public List<String> listDevices() {
-        List<Device> devices;
+    public List<DBusPath> listDevices() {
+        updateDevices();
+        return this.devices;
+    }
+
+    private void updateDevices() {
+        List<org.asamk.signal.manager.api.Device> linkedDevices;
         try {
-            devices = m.getLinkedDevices();
+            linkedDevices = m.getLinkedDevices();
         } catch (IOException | Error.Failure e) {
             throw new Error.Failure("Failed to get linked devices: " + e.getMessage());
         }
 
-        return devices.stream().map(d -> d.getName() == null ? "" : d.getName()).collect(Collectors.toList());
+        unExportDevices();
+
+        linkedDevices.forEach(d -> {
+            final var object = new DbusSignalDeviceImpl(d);
+            final var deviceObjectPath = object.getObjectPath();
+            try {
+                connection.exportObject(object);
+            } catch (DBusException e) {
+                e.printStackTrace();
+            }
+            if (d.isThisDevice()) {
+                thisDevice = new DBusPath(deviceObjectPath);
+            }
+            this.devices.add(new DBusPath(deviceObjectPath));
+        });
+    }
+
+    private void unExportDevices() {
+        this.devices.stream().map(DBusPath::getPath).forEach(connection::unExportObject);
+        this.devices.clear();
     }
 
     @Override
-    public void updateDeviceName(String deviceName) {
-        try {
-            m.updateAccountAttributes(deviceName);
-        } catch (IOException | Signal.Error.Failure e) {
-            throw new Error.Failure("UpdateAccount error: " + e.getMessage());
-        }
+    public DBusPath getThisDevice() {
+        updateDevices();
+        return thisDevice;
     }
 
     @Override
@@ -759,4 +787,53 @@ public class DbusSignalImpl implements Signal {
     private String nullIfEmpty(final String name) {
         return name.isEmpty() ? null : name;
     }
+
+    private static String getDeviceObjectPath(String basePath, long deviceId) {
+        return basePath + "/Devices/" + deviceId;
+    }
+
+    public class DbusSignalDeviceImpl extends DbusProperties implements Signal.Device {
+
+        private final org.asamk.signal.manager.api.Device device;
+
+        public DbusSignalDeviceImpl(final org.asamk.signal.manager.api.Device device) {
+            super();
+            super.addPropertiesHandler(new DbusInterfacePropertiesHandler("org.asamk.Signal.Device",
+                    List.of(new DbusProperty<>("Id", device::getId),
+                            new DbusProperty<>("Name",
+                                    () -> device.getName() == null ? "" : device.getName(),
+                                    this::setDeviceName),
+                            new DbusProperty<>("Created", device::getCreated),
+                            new DbusProperty<>("LastSeen", device::getLastSeen))));
+            this.device = device;
+        }
+
+        @Override
+        public String getObjectPath() {
+            return getDeviceObjectPath(objectPath, device.getId());
+        }
+
+        @Override
+        public void removeDevice() throws Error.Failure {
+            try {
+                m.removeLinkedDevices(device.getId());
+                updateDevices();
+            } catch (IOException e) {
+                throw new Error.Failure(e.getMessage());
+            }
+        }
+
+        private void setDeviceName(String name) {
+            if (!device.isThisDevice()) {
+                throw new Error.Failure("Only the name of this device can be changed");
+            }
+            try {
+                m.updateAccountAttributes(name);
+                // update device list
+                updateDevices();
+            } catch (IOException e) {
+                throw new Error.Failure(e.getMessage());
+            }
+        }
+    }
 }