Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

allow DBus data type='aay' #368

Closed
wants to merge 3 commits into from
Closed
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -3,3 +3,4 @@
pubspec.lock
test/.test_cov.dart
coverage/
/test/widget_test.dart
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Doesn't seem to be used.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This was me trying to learn AndroidStudio (click this, click that, suddenly files show up) to run the tests.

72 changes: 64 additions & 8 deletions lib/src/dbus_read_buffer.dart
Original file line number Diff line number Diff line change
@@ -171,10 +171,28 @@ class DBusReadBuffer extends DBusBuffer {
return null;
}

//RSC reading an aay signature
// In the case of a type aay coming from the dbus, the data will occur like this.
// Assuming a single "ay" byte array (len 10 of char 'x') inside an "a" we will get
// 14,0,0,0,10,0,0,0,x,x,x,x,x,x,x,x,x,x
// This makes sense.
// If there were multiple 'ay' inside the a it would still work...
// e.g. if we had 2 ay's in the a (1 len 4), the other (len 6)
// 18,0,0,0,4,0,0,0,x,x,x,x,6,0,0,0,y,y,y,y,y,y

//print(' <<<< RSC dbus_read_buffer.readMessage signature [${signature}]\n ${_data}\n');

// The issue here is that we need to calculate that initial length after
// iterating over the possible nested array elements to build our DBusValueType's correctly.
// e.g. we want DBusArray(DBusSignature('a'),DBusArray.bytes(bytevalues))
// instead of DBusArray(DBusSignature('a'),[]), DBusArray.byte[byte values]
// which is what it currently returns (although this works)

var dataEnd = readOffset + dataLength;
var values = <DBusValue>[];
if (signature != null) {
var signatures = signature.split();
List<DBusValue> valueStack = <DBusValue>[];
for (var s in signatures) {
var value = readDBusValue(s, endian, fdCount);
if (value == null) {
@@ -183,8 +201,37 @@ class DBusReadBuffer extends DBusBuffer {
if (readOffset > dataEnd) {
throw 'Message data of size $dataLength too small to contain ${signature.value}';
}
values.add(value);
if (s.value == 'a') {
// Specifically, this indicates an array of arrays. e.g. type='aay'
// which we need to translate into
// DBusArray(DBusSignature('a'),[DBusArray(DBusSignature(y),[DBusByte(42),DBusByte(99)])])
// -or- more succinctly
// DBusArray(DBusSignature('a'),[DBusArray.byte([42,99])])
//
// The 'a' sig value returned above is an empty DBusArray(DBusSignature('a'),[])
// This will become the outter array later when they are all combined.
valueStack.add(value);
} else {
//If valueStack has anything in it, this is the second array in an array of arrays.
if ((value is DBusArray)&&(valueStack.length>0)) {
valueStack.add(value);
}
//Unwind any array of array values before adding the value to the values
if (valueStack.length>0) {
//print(' RSC COMBINING ARRAY OF ARRAYS DATA');
DBusArray outterArr = valueStack[0] as DBusArray;
for (int ndx=1;ndx<valueStack.length;ndx++) {
outterArr.children.add(valueStack[ndx]);
}
values.add(outterArr);
valueStack.clear();
} else {
values.add(value);
}
}
}
if (valueStack.length>0) throw "SOMEONE DID NOT CLEAN THE valueStack";
//RSC REVISIT
if (readOffset != dataEnd) {
throw 'Message data of size $dataLength too large to contain ${signature.value}';
}
@@ -450,12 +497,15 @@ class DBusReadBuffer extends DBusBuffer {

var end = readOffset + length.value;
var children = <DBusValue>[];
while (readOffset < end) {
var child = readDBusValue(childSignature, endian, fdCount);
if (child == null) {
return null;
//RSC If it is an a then just return the empty children and they will be filled in later.
if (childSignature.value != 'a') {
while (readOffset < end) {
var child = readDBusValue(childSignature, endian, fdCount);
if (child == null) {
return null;
}
children.add(child);
}
children.add(child);
}

return DBusArray(childSignature, children);
@@ -532,8 +582,14 @@ class DBusReadBuffer extends DBusBuffer {
}
return readDBusDict(keySignature, valueSignature, endian, fdCount);
} else if (s.startsWith('a')) {
return readDBusArray(
DBusSignature(s.substring(1, s.length)), endian, fdCount);
// RSC aay should come in as an a then an ay
// So if this is just the 'a' then return just read that.
if (s.length==1) {
return readDBusArray(
DBusSignature(s.substring(0, s.length)), endian, fdCount);
} else
return readDBusArray(
DBusSignature(s.substring(1, s.length)), endian, fdCount);
} else if (s.startsWith('(') && s.endsWith(')')) {
return readDBusStruct(
DBusSignature(s.substring(1, s.length - 1)).split(), endian, fdCount);
35 changes: 30 additions & 5 deletions lib/src/dbus_value.dart
Original file line number Diff line number Diff line change
@@ -629,6 +629,13 @@ class DBusSignature extends DBusValue {

/// Check [value] contains a valid signature and return the index of the end of the current child signature.
int _validate(String value, int index) {
//RSC REVISIT where this can happen
//print(' _validate called val:'+value+' index:'+index.toString());
if (index>=value.length) {
print('RSC: INDEX EXCEEDED because aa passed to constructor of DBusSignature');
return value.length-1;
}

if (value.startsWith('(', index)) {
// Struct.
var end = _findClosing(value, index, '(', ')');
@@ -659,10 +666,17 @@ class DBusSignature extends DBusValue {
}
return end;
} else if (value.startsWith('a', index)) {
// Array.
if (index >= value.length - 1) {
throw ArgumentError.value(value, 'value', 'Array missing child type');
//RSC commented
//if (index >= value.length - 1) {
// throw ArgumentError.value(value, 'value', 'Array missing child type');
//}
if (value.length==1) {
//RSC So it can be just an "a" with no follow on type
//print(' RSC _validate HACK ALLOW "a"');
return 1;
}
//RSC REVISIT:
// Shouldnt we else with the original code??? dbus_write_buffer.writeMessage
return _validate(value, index + 1);
} else if (value.startsWith('m', index)) {
// Maybe.
@@ -687,6 +701,14 @@ class DBusSignature extends DBusValue {
} else if (value.startsWith('a{', index)) {
return _findClosing(value, index, '{', '}');
} else if (value.startsWith('a', index)) {
//RSC check for aa as in 'aay' or 'aas'
String rest = value.substring(index);
if (rest.length>1) {
if (rest.substring(1,2)=='a') {
//print('RSC: dbus_value._findChildSignatureEnd found an array of arrays in signature [${value}] index: ${index}');
return index; //only return index because caller will add 1 to it
}
}
return _findChildSignatureEnd(value, index + 1);
} else if (value.startsWith('m', index)) {
return _findChildSignatureEnd(value, index + 1);
@@ -891,13 +913,16 @@ class DBusArray extends DBusValue {
/// An exception will be thrown if a DBusValue in [children] doesn't have a signature matching [childSignature].
DBusArray(this.childSignature, [Iterable<DBusValue> children = const []])
: children = children.toList() {
if (!childSignature.isSingleCompleteType) {
//RSC it could be just an 'a'
// if (!childSignature.isSingleCompleteType) {
if ((childSignature.value != 'a')&&(!childSignature.isSingleCompleteType)) {
throw ArgumentError.value(childSignature, 'childSignature',
'Array value type must be a single complete type');
}

for (var child in children) {
if (child.signature.value != childSignature.value) {
//RSC same as above
if ((childSignature.value != 'a')&&(child.signature.value != childSignature.value)) {
throw ArgumentError.value(children, 'children',
"Provided children don't match array signature ${childSignature.value}");
}
22 changes: 21 additions & 1 deletion lib/src/dbus_write_buffer.dart
Original file line number Diff line number Diff line change
@@ -68,7 +68,10 @@ class DBusWriteBuffer extends DBusBuffer {
if (message.values.isNotEmpty) {
var signature = '';
for (var value in message.values) {
signature += value.signature.value;
//RSC Build sig recursively in the case of aay
String walkVal = walkForSignature(value);
//signature += value.signature.value;
signature = signature + walkVal;
}
headers.add(_makeHeader(8, DBusSignature(signature)));
}
@@ -80,6 +83,23 @@ class DBusWriteBuffer extends DBusBuffer {
align(8);
writeBytes(valueBuffer.data);
_resourceHandles.addAll(valueBuffer.resourceHandles);
//RSC
//print(' >>>>dbus_write_buffer.writeMessage data\n[${data}]\n');
}

//RSC recurse for signature (but should only do 1 extra level) REVISIT
String walkForSignature(DBusValue dval) {
//print(' ## RSC:walk runtimeType: ${dval.runtimeType}');
if ((dval is DBusArray)&&(dval.childSignature.value == 'a')) {
//array of array..e.g. aay or aas would hit here first.
//print(' ## RSC:walk adding "a" (array of arrays)');
//In writeMessage the DBusValue array of arrays would have children.
//if not then there is an error.
if ((dval.children).isEmpty) throw "Empty Array of Arrays found...${dval}";
return 'a' + walkForSignature(dval.children[0]);
}
//Something like ay (byte array) or as (string array)
return dval.signature.value;
}

/// Makes a new message header.
9 changes: 7 additions & 2 deletions test/dbus_test.dart
Original file line number Diff line number Diff line change
@@ -518,9 +518,14 @@ void main() {
// Too long.
expect(() => DBusSignature('s' * 256), throwsArgumentError);
// Missing array type.
expect(() => DBusSignature('a'), throwsArgumentError);
//RSC now allow 'a' signature in array. DBus signature aa is
//still bad but the way this test is run it fails because of the
//way _verify walks the signature.
//REVISIT
//expect(() => DBusSignature('a'), throwsArgumentError);
//expect(() => DBusSignature('aa'), throwsArgumentError);

expect(() => DBusSignature('(a)'), throwsArgumentError);
expect(() => DBusSignature('aa'), throwsArgumentError);
expect(() => DBusSignature('a{sa}'), throwsArgumentError);
// Missing maybe type.
expect(() => DBusSignature('m'), throwsArgumentError);
374 changes: 374 additions & 0 deletions test/test-aay.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,374 @@
import 'package:dbus/code_generator.dart';
import 'package:dbus/dbus.dart';
import 'package:dbus/src/dbus_bus_name.dart';
import 'package:dbus/src/dbus_error_name.dart';
import 'package:dbus/src/dbus_interface_name.dart';
import 'package:dbus/src/dbus_match_rule.dart';
import 'package:dbus/src/dbus_member_name.dart';
import 'package:dbus/src/dbus_message.dart';
import 'package:dbus/src/dbus_write_buffer.dart';
import 'package:dbus/src/dbus_uuid.dart';
import 'package:dbus/src/getuid.dart';
import 'package:dbus/src/dbus_value.dart';

import 'dart:convert';
import 'dart:io';

DBusClient systemDBusClient = DBusClient.system();
//These get filled in in
late DBusRemoteObject serverObject;
late DBusRemoteObject entryGroupObject;
AvahiServiceConfig serviceConfig = AvahiServiceConfig('trixy',8765);

void main() async {

print('TEST Googles NearbyShare (Advertising) using Avahi via DBus on Linux.');
print(" *This requires DBus data type='aay' (array of array of bytes).\nSteps.");

print('1. Attach to Avahi Server using the Dart DBus lib.');
print('2. Create an Avahi EntryGroup object.');
print('3. Add a NearbyShare Service definition to the new EntryGroup.');
print('4. Poll the EntryGroup status until the new service is ESTABLISHED');
print('5. Call Avahi ResolveService via DBus to print content of our');
print(' newly added NearbyShare (advertising) service.\n');
print('BEGIN TEST ON Platform.hostname: ${Platform.localHostname}');

await attachAvahiObjects();

await testAddAvahiService();

await testResolveAvahiService();

print('DONE\n');
print('''
If there were no errors, You should be able see the Nearby Share named
${serviceConfig.fullAdvertisedName}
from an android device or any Nearby Share client''');
print('\nPRESS ENTER TO QUIT (removes service)...');

var line = stdin.readLineSync(encoding: utf8);
print('line: ${line}');
}

/* SAMPLE AVAHI SERVICE FILE
<service-group>
<!-- Advertise ourselves using mDNS as a Google NearDrop Advertiser/Server
(As Per https://github.com/grishka/NearDrop/blob/master/PROTOCOL.md)
**Client side service name comes from TXT record and will show us as
"roxypuke" on client side.
**mDNS advertised name is special format as per PROTOCOL.md and shows up as
"I1NISVT8n14AAA"
$ avahi-browse -r _FC9F5ED42C8A._tcp
-->
<!-- mDNS advertised name of service
Hex or Char Base64 | Base64URLSafe
0x23, IW== | Iw
4 random chars "SHIT", U0hJVA== | U0hJVA
3 chars 0xFC, 0x9F, 0x5E, /J9e | _J9e
2 0x00 chars AAA= | AAA
Full Hex: 23 53 48 49 54 FC 9F 5E 00 00
Base64: I1NISVT8n14AAA==
Base64url: I1NISVT8n14AAA
-->
<!--<name replace-wildcards="yes">Dog on %h</name>-->
<name>I1NISVT8n14AAA</name>
<service protocol="ipv4">
<domain-name></domain-name><!--yes empty domain-name is necessary...-->
<type>_FC9F5ED42C8A._tcp</type>
<port>8088</port>
<!--
TXT record n="base64url encoded string"
byte content:
b1:000-version,0-visibility,011-laptop,0-reserved (00000110)=0x06
b2..b17 random hex 0x06,0x09,0x06,0x09,0x06,0x09,0x06,0x09,0x06,0x09,0x06,0x09,0x06,0x09,0x06,0x09
b18 (length of name of service as it will show up on the client nearby share screen "roxypuke")
b19..b19+b18length user visible device name...roxypuke le 8
Hex content:
0x06,
0x06,0x09,0x06,0x09,0x06,0x09,0x06,0x09,0x06,0x09,0x06,0x09,0x06,0x09,0x06,0x09,
0x08,
0x72,0x6f,0x78,0x79,0x70,0x75,0x6b,0x65
Full Hex: 0x06,0x06,0x09,0x06,0x09,0x06,0x09,0x06,0x09,0x06,0x09,0x06,0x09,0x06,0x09,0x06,0x09,0x08,0x72,0x6f,0x78,0x79,0x70,0x75,0x6b,0x65
b64: BgYJBgkGCQYJBgkGCQYJBgkIcm94eXB1a2U=
b64Url: BgYJBgkGCQYJBgkGCQYJBgkIcm94eXB1a2U
-->
<txt-record>n="BgYJBgkGCQYJBgkGCQYJBgkIcm94eXB1a2U"</txt-record>
</service>
</service-group>
*/

// This class AvahiServiceConfig Config is a placeholder for props used to Advertise
// our Nearby Share service using mDNS on Avahi daemon on local LAN.

class AvahiServiceConfig {
AvahiServiceConfig(this.nearbyAdvertisedName,this.port,
{this.domain='local',
this.host='',
this.interface=-1,
this.protocol=0,
this.flags=0}
);

final String serviceType = '_FC9F5ED42C8A._tcp';
//The service Type is a fixed string. Check the PROTOCOL for why.
final String serviceName = 'I1NISVT8n14AAA';
// The service Name is fixed here but we could allow the configuration of the
// four random characters...its not really worth it so I just use "SHIT"
// Hex or Char Base64 | Base64URLSafe
// 0x23, IW== | Iw
// 4 random chars "SHIT", U0hJVA== | U0hJVA
// 3 chars 0xFC, 0x9F, 0x5E, /J9e | _J9e
// 2 0x00 chars AAA= | AAA
// Full Hex: 23 53 48 49 54 FC 9F 5E 00 00
// As Base64: I1NISVT8n14AAA==
// As Base64url: I1NISVT8n14AAA

int port; //Port number for nearby share server to listen on.
String domain; //On a local linux lan this would be "local"
String host; //fully qualified e.g. roxy.local
int interface; //-1 all interfaces
int protocol; //0 unspecified
int flags; //0 unspecified

String nearbyAdvertisedName; // see ctor.

//If you pass a host name in, it needs to be fully qualified...e.g. myhost.local
String get nearbyAdvertisedHost {
if (this.host.trim().length == 0) {
if (Platform.localHostname.contains('.')) return Platform.localHostname;
else return Platform.localHostname+'.'+this.domain;
} else return this.host.trim();
}

String get fullAdvertisedName {
String s = this.nearbyAdvertisedHost+':'+this.nearbyAdvertisedName; //TODO add HOST
//String s = this.nearbyAdvertisedName; //Dont include host
return s;
}
// Return base64url encoded string of byte(val=0x06) + random bytes + nearbyAdvertisedName
// byte content:
// b1:000-version,0-visibility,011-laptop,0-reserved (00000110)=0x06
// b2..b17 random hex 0x06,0x09,0x06,0x09,0x06,0x09,0x06,0x09,0x06,0x09,0x06,0x09,0x06,0x09,0x06,0x09
// b18 (length of name of service as it will show up on the client nearby share screen "roxypuke")
// b19..(b19+b18) length = user visible device name...nearbyAdvertisedHost:nearbyAdvertisedName
// Note that a TXT record is an array of arrays but for a NearbyShare service it is only
// one string.
// e.g. n="base64url encoded string based on byte(val=0x06) + random bytes + nearbyAdvertisedName"
// There is no occassion (in this service definition) for actual array of array.
// e.g. x=y,a=b
// This method returns the string based on byte(val=0x06) + random bytes + nearbyAdvertisedName"

String getSpecialEncodedFullAdvertisedName() {

List<int> fan = utf8.encode(this.fullAdvertisedName);
//cant be more than 256 bytes since length is single byte...
List<int> byteArr = [6,6,9,6,9,6,9,6,9,6,9,6,9,6,9,6,9,fan.length];
byteArr.addAll(fan);
// Should have something like this....
// 'BgYJBgkGCQYJBgkGCQYJBgkIcm94eXB1a2U=='
return base64Url.encode(byteArr);
}

// TXT Record notes. 1st and ONLY byte array of the TXT record.
//As per PROTOCOL.md from github NearDrop repo.
//The TXT record of the mdns record contains the "Advertised Name" plus other
//odd data as described above all globbed together and base64url'd.
//We combine nearbyAdvertisedHost+':'+nearbyAdvertisedName as the
// "Full Advertised Name" that a user connecting to us would see
// e.g. "myhost.local:My Cool Nearby Share Name"
// Should return something like n="BgYJBgkGCQYJBgkGCQYJBgkIcm94eXB1a2U=="
String get txt {
return('n="'+this.getSpecialEncodedFullAdvertisedName()+'"');
}

@override String toString() {
return
'''AvahiServiceConfig(
fullAdvertiseName: '${fullAdvertisedName}',
port: ${port}, serviceName: '${serviceName}', serviceType: '${serviceType}',
domain: '${domain}', host: '${nearbyAdvertisedHost}',
interface: ${interface}, protocol: ${protocol}, flags: ${flags},
txt: '${this.txt}'
)
''';
}
List<DBusValue> asAvahiAddServiceParamList() {
List<int> txtAsBytes = utf8.encode(this.txt);
List<DBusByte> txtAsByteList = [];
txtAsBytes.forEach((bv) {
txtAsByteList.add(DBusByte(bv));
});
DBusArray txtArr = DBusArray(DBusSignature('y'),txtAsByteList);

var parmValues = [
DBusInt32(this.interface), //interface 3
DBusInt32(this.protocol), //protocol
DBusUint32(this.flags), //flags 13
DBusString(this.serviceName), //name
DBusString(this.serviceType), //type [DO NOT CHANGE]
DBusString(this.domain), // domain
DBusString(this.nearbyAdvertisedHost), // host (TODO add domain?)
DBusUint16(this.port), //8019), //port
DBusArray(DBusSignature('a'),[txtArr]),
];

return parmValues;
}

List<DBusValue> asAvahiResolveServiceParamList() {
var parmValues = [
DBusInt32(this.interface), //interface 3
DBusInt32(this.protocol), //protocol
DBusString(this.serviceName), //name
DBusString(this.serviceType), //type [DO NOT CHANGE]
DBusString(this.domain), // domain
DBusInt32(-1), //aprotocol?
DBusUint32(this.flags), //flags
];
return parmValues;
}

}


Future<void> testResolveAvahiService() async {
print(" --< testResolveAvahiService");
//Test ResolveService call
//dbus-send --system --print-reply --type=method_call --dest=org.freedesktop.Avahi /
// org.freedesktop.Avahi.Server.ResolveService int32:-1 int32:-1
// string:"I1NISVT8n14AAA" string:"_FC9F5ED42C8A._tcp" string:"" int32:-1 uint32:0

try {

var res = await serverObjectInvoke('ResolveService',serviceConfig.asAvahiResolveServiceParamList());
print(' --< ResolveService results: ${res}');

} catch (ee) {
print('--< RESOLVE SERVICE ERROR ${ee}');
}
}

//From avahi source interface org.freedesktop.Avahi.EntryGroup
//<method name="AddService">
//<arg name="interface" type="i" direction="in"/>
//<arg name="protocol" type="i" direction="in"/>
//<arg name="flags" type="u" direction="in"/>
//<arg name="name" type="s" direction="in"/>
//<arg name="type" type="s" direction="in"/>
//<arg name="domain" type="s" direction="in"/>
//<arg name="host" type="s" direction="in"/>
//<arg name="port" type="q" direction="in"/>
//<arg name="txt" type="aay" direction="in"/>
//</method>
//Test adding a service to the Avahi daemon using DBus.
//This assumes that attachAvahiObjects has already run.

Future<void> testAddAvahiService() async {

print(' --* Testing AddService to Entry Group ${entryGroupObject.path}');
print(' --* AVAHI SERVICE CONFIG: ${serviceConfig}');
print(' --* AVAHI ADD SERVICE PARMS: ${serviceConfig.asAvahiAddServiceParamList()}');

try {
//assume that an EntryGroup was already added in attachAvahiObjects()

var aRes = await entryGroupObjectInvoke('GetState',<DBusValue>[]);
print(' --* GetState results: ${aRes}');

var bRes = await entryGroupObjectInvoke('AddService',serviceConfig.asAvahiAddServiceParamList());
print(' --* AddService results: ${bRes}');

var cRes = await entryGroupObjectInvoke('Commit',<DBusValue>[]);
print(' --* Commit results: ${cRes}');

var dRes = await entryGroupObjectInvoke('GetState',<DBusValue>[]);
print(' --* GetState results: ${dRes}');

var eRes = await entryGroupObjectInvoke('IsEmpty',<DBusValue>[]);
print(' --* IsEmpty results: ${eRes}');

//typedef enum {
// AVAHI_ENTRY_GROUP_UNCOMMITED, /**< The group has not yet been committed, the user must still call avahi_entry_group_commit() */
// AVAHI_ENTRY_GROUP_REGISTERING, /**< The entries of the group are currently being registered */
// AVAHI_ENTRY_GROUP_ESTABLISHED, /**< The entries have successfully been established */
// AVAHI_ENTRY_GROUP_COLLISION, /**< A name collision for one of the entries in the group has been detected, the entries have been withdrawn */
// AVAHI_ENTRY_GROUP_FAILURE /**< Some kind of failure happened, the entries have been withdrawn */
//} AvahiEntryGroupState;
int curState = (dRes[0] as DBusInt32).value;
while (curState<2) {
print(' --* LOOP curState: ${curState}');
var lRes = await entryGroupObjectInvoke('GetState',<DBusValue>[]);
curState = (lRes[0] as DBusInt32).value;
await Future.delayed(Duration(seconds: 2));
}
print(' --* LOOP FINISHED STATE: ${curState}');
print(' --* SERVICE ESTABLISHED');
} catch (ee) {
print('--* ADD SERVICE ERROR ${ee}');
}
}


Future<void> attachAvahiObjects() async {
try {
print('--> Attaching Avahi Server and EntryGroup objects');
serverObject = DBusRemoteObject(systemDBusClient,
name: 'org.freedesktop.Avahi',
path: DBusObjectPath('/')
);

//EntryGroupNew call
//dbus-send --system --print-reply --type=method_call --dest=org.freedesktop.Avahi /
// org.freedesktop.Avahi.Server.EntryGroupNew

var nbs_results = await serverObjectInvoke('EntryGroupNew',<DBusValue>[]);
print(' -->EntryGroupNew results: ${nbs_results}');

DBusObjectPath entryGroupPath = nbs_results[0] as DBusObjectPath;
print(' -->New EntryGroupPath: ${entryGroupPath}');

entryGroupObject = DBusRemoteObject(systemDBusClient,
name: 'org.freedesktop.Avahi',
path: entryGroupPath
);

var eg_results = await entryGroupObjectInvoke('GetState',<DBusValue>[]);
int egState = (eg_results[0] as DBusInt32).value;
print(' -->GetState result: ${egState}');

print('-->Avahi objects attached.');
} catch (ae) {
print('-->ATTACH ERROR: ${ae}');
}
}

// UTILITY FUNCTIONS BELOW

Future<List<DBusValue>> serverObjectInvoke(methodName,args) async {
final interfaceName = 'org.freedesktop.Avahi.Server';
var result = await serverObject.callMethod(
interfaceName, methodName, args
);
return result.returnValues;
}
Future<List<DBusValue>> entryGroupObjectInvoke(methodName,args) async {
final interfaceName = 'org.freedesktop.Avahi.EntryGroup';
var eg_result = await entryGroupObject.callMethod(
interfaceName, methodName, args
);
//print('RAW RESULT ${eg_result}');
return eg_result.returnValues;
}


111 changes: 111 additions & 0 deletions test/test-array-of-arrays.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
import 'package:dbus/code_generator.dart';
import 'package:dbus/dbus.dart';
import 'package:dbus/src/dbus_bus_name.dart';
import 'package:dbus/src/dbus_error_name.dart';
import 'package:dbus/src/dbus_interface_name.dart';
import 'package:dbus/src/dbus_match_rule.dart';
import 'package:dbus/src/dbus_member_name.dart';
import 'package:dbus/src/dbus_message.dart';
import 'package:dbus/src/dbus_write_buffer.dart';
import 'package:dbus/src/dbus_uuid.dart';
import 'package:dbus/src/getuid.dart';
import 'package:dbus/src/dbus_value.dart';

import 'dart:convert';
import 'dart:io';


void main() async {
print('TEST new array of arrays DBus types');

//type=iaayi
var test_aay = [
DBusInt32(42),
DBusArray(DBusSignature('a'), [
DBusArray(DBusSignature('y'), [DBusByte(88), DBusByte(88), DBusByte(88)])
]),
DBusInt32(69),
];
print('TEST Signature [iaayi]');
dumpAsMessage(test_aay);

//type = iaasi
var test_aas = [
DBusInt32(42),
DBusArray(DBusSignature('a'), [
DBusArray(DBusSignature('s'),
[DBusString('XXX'), DBusString('YYY'), DBusString('ZZZ')])
]),
DBusInt32(69),
];
print('TEST Signature [iaasi]');
dumpAsMessage(test_aas);

//type = iaayaasi
var test_both = [
DBusInt32(42),
DBusArray(DBusSignature('a'), [
DBusArray(DBusSignature('y'), [DBusByte(88), DBusByte(88), DBusByte(88)]),
]),
DBusArray(DBusSignature('a'), [
DBusArray(DBusSignature('s'),
[DBusString('XXX'), DBusString('YYY'), DBusString('ZZZ')])
]),
DBusInt32(69),
];
print('TEST Signature [iaayaasi]');
dumpAsMessage(test_both);

// type=iasi
var test_str_only = [
DBusInt32(42),
DBusString('XXX'),
DBusInt32(69),
];
print('TEST Signature [isi]');
dumpAsMessage(test_str_only);

// type=ias
var test_str_no_trailing = [
DBusInt32(42),
DBusString('XXX'),
];
print('TEST Signature [is]');
dumpAsMessage(test_str_no_trailing);


var test_fail = [
DBusInt32(42),
DBusArray(DBusSignature('a'), [
DBusArray(DBusSignature('y'), [DBusByte(88), DBusByte(88), DBusByte(88)]),
DBusArray(DBusSignature('y'), [DBusByte(99), DBusByte(99), DBusByte(99)]),
]),
];
print('TEST Signature [iaay]');
dumpAsMessage(test_fail);

print('DONE\n');
}

void dumpAsMessage(List<DBusValue> valArr) {
print('++++Test fake message with values\n');
DBusMessage fakeMsg = DBusMessage(
DBusMessageType.methodCall,
path: null,
interface:null,
member:null,
errorName:null,
replySerial:null,
destination: null,
sender: null,
values: valArr,
);

DBusWriteBuffer dbwb = DBusWriteBuffer();
dbwb.writeMessage(fakeMsg);

print(' ++++Fake message with values\n'+fakeMsg.toString());
print(' ++++RAW fake message bytes\n${dbwb.data}');
//dbwb.data.forEach((b)=> print(b.runtimeType.toString()+' => '+b.toString()));
print('++++DONE Test fake message with values');
}