hypfvieh / dbus-java

Improved version of java DBus library provided by freedesktop.org (https://dbus.freedesktop.org/doc/dbus-java/)
https://hypfvieh.github.io/dbus-java/
MIT License
185 stars 73 forks source link

Properties.Set method call fails for parametrized value #91

Closed AleksejsGrocevs closed 4 years ago

AleksejsGrocevs commented 4 years ago
var params = new Object[]{"org.bluez.MediaTransport1", "Volume", new Variant<>(new UInt16(127))};
var mediaTransport = dBusConnection.getRemoteObject("org.bluez", currentTransportPath);
dBusConnection.callMethodAsync(mediaTransport, "Set", params);

The follwing object.getClass().getMethod(m, types); call in callMethodAsync will fail with NoSuchMethodException since types are resolved as [String.class, String.class, Variant.class] and Set method is defined as <A> void Set(String interface_name, String property_name, A value); hence wating for [String.class, String.class, Object.class] parameters to be resolved.

hypfvieh commented 4 years ago

I don't know what you want to achieve, but setting Properties this way is simply wrong.

You have to call the 'Set' method ON the mediaTransport object. The method you are calling is usually only used for signal and also mainly intended to be used internally.

So the correct way would be, getting the object on the bus and calling Set on that:

MediaTransport1 mediaTransport = dBusConnection.getRemoteObject("org.bluez", currentTransportPath, MediaTransport.class);

mediaTransport.Set("org.bluez.MediaTransport1", "Property", "Value");

For using Properties on objects (mainly use the Get methdo) take a look at an example in my sandbox repo, Line 43++

AleksejsGrocevs commented 4 years ago

I am trying to set the volume on the BT connected device using https://gist.github.com/parkerlreed/1a5e181fa7ed48200b5e426cf31d8c36 as an example.

I am not sure if MediaTransport.class is not a typo (I'm using MediaTransport1.class instead), but alas, getting similar error - org.freedesktop.dbus.errors.UnknownMethod: Method "Set" with signature "ssv" on interface "org.bluez.MediaTransport1" doesn't exist for mediaTransport.Set("org.bluez.MediaTransport1", "Volume", new Variant<>(new UInt16(50))); call.

By the way, the Get call (dBusConnection.getRemoteObject("org.bluez", currentTransportPath, MediaTransport1.class).Get("org.bluez.MediaTransport1", "Volume")) is also failing with same error - Method "Get" with signature "ss" on interface "org.bluez.MediaTransport1" doesn't exist.

However, the moment I switch to dBusConnection.getRemoteObject("org.bluez", currentTransportPath, Properties.class) all works like a charm - both Get and Set methods are OK to be called.

hypfvieh commented 4 years ago

This code is working:

        DBusConnection connection = DBusConnection.getConnection(DBusBusType.SYSTEM);

        String dbusPath = "/org/bluez/hci0/dev_YOUR_MAC_HERE/fd0";

        Properties remoteObject = connection.getRemoteObject("org.bluez", dbusPath, Properties.class);
        Object state = remoteObject.Get(MediaTransport1.class.getName(), "State");

        System.out.println("state: " + state);

        connection.close();

Please note, that not every BT device supports every property. My test-device (JBL Flip 4) does not support the "Volume" property. It can not be set or read, no matter if you try it with dbus-java or using d-feet or any other DBus util. Thats why I'm using 'State' instead of 'Volume' in this example.

AleksejsGrocevs commented 4 years ago
Properties remoteObject = connection.getRemoteObject("org.bluez", dbusPath, Properties.class);

That's what I mentioned in my last comment - it is still Properties remote object, not an actual MediaTransport instance. In regards to supported properties - luckily there is GetAll method to check available properties for the object.

What about the question about a strange inability to call a Set method on the MediaTransport1 interface directly? (org.freedesktop.dbus.errors.UnknownMethod: Method "Set" with signature "ssv" on interface "org.bluez.MediaTransport1" doesn't exist)

hypfvieh commented 4 years ago

Because it is the wrong interface class. Properties can only be accessed by the Properties interface when the correct interface name is given (in this case org.bluez.MediaTransport1).

The properties are not part of the object transfered by DBus, but maintained by the Properties interface instance managed by DBus.

The inheritance used in Java in this case is useless (and confusing/wrong).

Object properties may look like Members in Java, but DBus does not transfer Objects complete objects. DBus provides a serialization service which allow communication between different applications using the same kind of abstraction language.

So the transfered information are signals (events) and method calls (like in xml-rpc, rest or SOAP). Transfering properties is handled by the Properties interface. A application can register identifiers on this interface with registered interface names and DBus will then allow to get/set those identifiers.

AleksejsGrocevs commented 4 years ago

Thank you for clarification, indeed some DBus interaction parts are confusing. Anyhow, if you believe that my described approach/behavior is working as expected - you can probably close the issue.

PS. Maybe some additional info/examples in documentation could aid newcomers in the future (eg you could mention your sandbox somewhere).

hypfvieh commented 4 years ago

Your approach is working. You just need to know what you want to do. If you want to read/write properties, you have to use the Properties interface. If you want to call methods, you need the proper interface for that (in your case MediaTransport1).