2013-06-12 07:06:42

by Mikel Astiz

[permalink] [raw]
Subject: [RFC BlueZ v2 00/13] Add experimental org.bluez.Service1

From: Mikel Astiz <[email protected]>

Changes from v1:
- A redundante call to service_unregister() removed as pointed out by Gustavo.
- 'connecting' state exposed in D-Bus.

>From original cover-letter:

Beyond the desktop use-cases, some users (e.g. GENIVI) are interested in having profile-specific information and control interfaces exposed in D-Bus. Such APIs did exist in BlueZ 4 but were dropped for BlueZ 5 in favor of simpler API simplicity. This service-specific interfaces had a fairly low priority for BlueZ 5 and therefore the discussion was postponed.

This patchset proposes org.bluez.Service1 as an attempt to cover these needs. As compared to the former Device.ConnectProfile()/DisconnectProfile(), the approach has the following advantages:
- Multiple instances of the same UUID can be exposed.
- The state of each service can be exposed, without hackish lists like Device.ConnectedProfiles.
- It's ObjectManager-centric.
- The design should scale better if new properties are required in the future (supported features, service handle, etc.)

Mikel Astiz (13):
test: Remove obsolete test script
test: Add UUID alias table to bluezutils.py
test: Support human-friendly UUIDs in test-device
test: Show human-friendly UUIDs in list-devices
dbus: Add new org.bluez.Service1
dbus: Add Device property to org.bluez.Service1
dbus: Add UUID property to org.bluez.Service1
dbus: Add state property to org.bluez.Service1
dbus: Add Connect/Disconnect to org.bluez.Service1
doc: Add API documentation for org.bluez.Service1
dbus: Deprecate old profile-connecting API
test: Add test-service script
test: Add --uuid to test-service

Makefile.am | 2 +-
Makefile.tools | 2 +-
doc/device-api.txt | 4 +-
doc/service-api.txt | 53 ++++++++++++++
src/device.c | 14 ++--
src/service.c | 197 +++++++++++++++++++++++++++++++++++++++++++++++++++-
src/service.h | 2 +-
test/bluezutils.py | 84 ++++++++++++++++++++++
test/list-devices | 6 +-
test/simple-service | 128 ----------------------------------
test/test-device | 4 +-
test/test-service | 175 ++++++++++++++++++++++++++++++++++++++++++++++
12 files changed, 530 insertions(+), 141 deletions(-)
create mode 100644 doc/service-api.txt
delete mode 100755 test/simple-service
create mode 100755 test/test-service

--
1.8.1.4



2013-06-19 21:13:30

by Marcel Holtmann

[permalink] [raw]
Subject: Re: [RFC BlueZ v2 00/13] Add experimental org.bluez.Service1

Hi Mikel,

>>>>> Beyond the desktop use-cases, some users (e.g. GENIVI) are interested in having profile-specific information and control interfaces exposed in D-Bus. Such APIs did exist in BlueZ 4 but were dropped for BlueZ 5 in favor of simpler API simplicity. This service-specific interfaces had a fairly low priority for BlueZ 5 and therefore the discussion was postponed.
>>>>>
>>>>> This patchset proposes org.bluez.Service1 as an attempt to cover these needs. As compared to the former Device.ConnectProfile()/DisconnectProfile(), the approach has the following advantages:
>>>>> - Multiple instances of the same UUID can be exposed.
>>>>
>>>> how is this going to help anyone. I you do not know what your service object is, what are you planning to do with it.
>>>
>>> See list below for a summary of real use-cases.
>>>
>>>>
>>>>> - The state of each service can be exposed, without hackish lists like Device.ConnectedProfiles.
>>>>
>>>> Again, here I want to see clear example on why you want all these states. Everybody takes about wanting all the crazy interim states and then never ever uses any of them.
>>>
>>> Many well-known UIs show this information (i.e. which profiles are
>>> connected), including IVI examples or even the Android UI. A new
>>> property such as "Device1.ConnectedProfiles" could be introduced but
>>> see below for arguments against this approach.
>>
>> and many well-known UIs do not show this information. So what is the point exactly here. Maybe some UIs need to be brought into 2013 and being stuck in what we did 6 years ago.
>
> This should be a UI decision IMO, not something BlueZ should impose
> unless it's widely accepted.

I am pretty much against displaying pointless information that nobody can use anyway. If my parents can not make sense out of it, they should not be there. If a non-tech person has no idea what all this crazy profile talk means, then it should not be there. This is my simplicity approach here.

The goal should be to provide a solution that just works and not 100 knobs to tweak it around like a crazy monkey.

>> My question however was not about if a profile is connected or not, my question was why we always end up with stupid interim states like "connecting".
>
> I'm completely fine dropping the 'connecting' state, which I already
> suggested to Johan.
>
>>
>>>>> - It's ObjectManager-centric.
>>>>> - The design should scale better if new properties are required in the future (supported features, service handle, etc.)
>>>>
>>>> I don't think this actually will scale that nicely. There are limited set of common properties for all these profiles. I am really curious to see how this is going to be used and how it would improve things for applications.
>>>
>>> The currently existing API hits its limits very soon, even if a
>>> property such as "Device1.ConnectedProfiles" was introduced. Besides
>>> the multiple-UUID-instances case, assume the following potential
>>> scenarios:
>>>
>>> 1. That a user-setting (enabled/disabled) needs to be exposed per
>>> service (or UUID), just like Android does. And I don't think you want
>>> to have a Device1.EnabledProfiles property.
>>
>> I still do not agree with the fact that we need to disable profiles. However first order of business before I even consider allowing something like this is that we interop with ourselves on profiles (SDP record) updates and I have not seen anybody trying to handle this.
>
> We're not talking about changing the SDP record here, just about
> disconnecting profiles (and rejecting incoming connections). This is
> what Android does and also our cars (generally speaking).

You brought up enabling and disabling profiles. And even if you just reject new connections, it is the same discussion. Actually having the record and the profile not available is even worse. There is also a attribute for showing profile availability details.

My major point is that we need to improve the way we deal with the situations when SDP databases change or single attributes of a record change. It seems something that everybody wants to swipe under the rug.

What I am saying is that if you want to do anything like this in this area, then there are 2 things that need to be done.

a) We need to update BlueZ to handle this gracefully since we want to interop with ourselves at least.

b) Check what other major Bluetooth stack from major OSes are doing in this area. And more important document their behavior. So how does iOS deal with PAN, PBAP and MAP rejections. Are they indicating them in the SDP record. How about Android, how about Windows, how about OS X.

We do a lot of handwaving and wanting to add new APIs, but we do not have enough data to judge what is really needed as a configuration option and what can just be done automatically. The lazy way out by just adding a new property or option is never something that I agree to likely. Feel free to proof me wrong. You have all the tools in your hand to document the behavior of other stacks.

>>> 2. That the 'connecting' state needs to be exposed in D-Bus. You'd
>>> otherwise need something like Device1.ConnectingProfiles.
>>
>> My point is that we do not need the connecting state at all. What is it good for?
>
> This is an advanced topic and as I said, I would be fine dropping this
> right now.
>
>>
>>> 3. That a D-Bus clients needs to know if a UUID is connectable,
>>> meaning not only supported but also probed (e.g. for "hfp", to
>>> distinguish whether oFono is running). You'd otherwise need something
>>> like Device1.ProbedProfiles.
>>
>> Has this really been thought through. What happens if a Device1.Connect is triggered and then oFono starts later on. What is the expected behavior here?
>
> Your questions needs to be answered regardless of org.bluez.Service1.
> In a similar way, crashes in oFono could be handled by reconnecting
> the profile automatically. I don't see this coupled in any way to this
> proposal.
>
> My point here was that the proposed D-Bus interface could express this
> information nicely, in case the caller of ConnectProfile() knows it
> will surely fail.

We never intended for ConnectProfile to be used that much. I assumed we just have Device1.Connect and then let BlueZ handle the rest. Individual profile connections were just for profiles without auto-connect.

>>> 4. That a human-friendly name needs to be exposed in D-Bus per service
>>> (this seemed to be an argument in favor of the Network1 interface).
>>
>> And what name is that exactly? How do you handle translation?
>
> A translation is also needed for profile-specific D-Bus interfaces.
> You need to know that the UUID of PAN translates to
> "org.bluez.Network1".
>
> Having said this, I'm not particularly in favor of doing this. D-Bus
> interfaces should be compact and without redundancies.

Of course you need to know which UUID translate to which interface. However that is not what I am talking about. You mention a human-friendly name. That name needs to be translatable if it represents something in a human language.

>>> 5. That additional remote information needs to be exposed, such as
>>> profile version or supported features.
>>
>> For what usage?
>
> To show this in the UI, in some advanced tab, or as part of a debugging tool.

The D-Bus API we have is not really a debug tool API. It is a main consumer UI.

Why would be an advanced tab a good idea. Who would be using such an advanced tab.

>>> Having an object-oriented Service1 API is IMO much cleaner as opposed
>>> to a sparse interface consisting of methods like
>>> ConnectProfile()/DisconnectProfile() along with properties like
>>> "ConnectedProfiles"/"ConnectingProfiles"/"EnabledProfiles"/"ProbedProfiles"
>>> which btw are string lists, therefore not very client-friendly.
>>>
>>> All these needs would be nicely addressed by a generic service D-Bus
>>> interface as proposed here. Note that some of the points above are
>>> real IVI requirements, and we're in fact having trouble to upgrade to
>>> BlueZ 5 due to API limitations. Some others might not be required
>>> after all or might have different workarounds, but I think it's fair
>>> enough to claim that the approach can scale better.
>>
>> If you need special interfaces, there is always the possibility of a vendor plugin using com.bmw or org.genivi interfaces.
>
> Generally speaking, I don't think it's a good idea to encourage
> plugins to expose custom D-Bus API extensions. Unless it's a very
> specific feature which nobody else could ever benefit from, which I
> don't think is the case.
>
> Regarding genivi specifically, there's no way a consortium will pick
> up an API that is not part of the official release.

That is hilarious :)

> Marking the interface as experimental already makes it non-stable and
> non-default so I don't see why it should be so controversial to merge
> it upstream. The code is fairly non-intrusive.

It is not about how intrusive or non-intrusive the code is. The extra interface must make sense for more than one user.

>>>> My initial take is still that just calling a general Device1.Connect or requesting a connect to a UUID is useful. Everything else is pretty much application specific anyway.
>>>>
>>>> You can always add profile specific interfaces. Like we do with Network1. Why are you saying your approach is more object manager centric.
>>>
>>> As argued above, it's presumably more object-oriented and therefore
>>> more ObjectManager-centric.
>>>
>>> Adding interfaces such as Network1 is IMO a step back in the direction
>>> to BlueZ 4 APIs, probably biased by the fact that the same developer
>>> community is involved in Connman. Recent commits
>>> (0624791ea6e917d6c9ecb8e7e6e5a1327199448d) mention that the
>>> Connect/Disconnect interface was brought back "for convenience", which
>>> should not be the driving factor when designing D-Bus APIs. This very
>>> same argument was in fact used to drop "convenience" APIs such as
>>> org.bluez.Manager (commit 86a7b07c22f3a595ba3c48092359287905bf0878).
>>>
>>> The Network1 interface could be easily deprecated in favor of a more
>>> generic Service1 interface. If we're going to introduce interfaces
>>> such as Headset1 etc., we should have stayed with BlueZ interfaces and
>>> avoid breaking the API. A quick look at these old audio interfaces
>>> (Headset, AudioSource, etc.) shows that Service1 could cover most of
>>> them if not all.
>>
>> Network1 can not be deprecated for the simple reason that it is special. It returns a network interface. You saw my comments on why we left it in for BlueZ 5.
>
> You're right, we couldn't completely remove the interface. You could
> however deprecate the Connect()/Disconnect() methods because they are
> redundant with ConnectProfile()/DisconnectProfile() or
> org.bluez.Service1. There's already a property informing about the
> interface name.

We can not. The return the interface information in a race free way. Feel free to go back and read the discussion between Johan and I on this subject.

>>> Furthermore, if you consider external profiles, it makes no sense to
>>> expose a profile-specific interface in BlueZ, since there's no way
>>> they could have profile-specific methods. org.bluez.Service1 fits
>>> again nicely in this case as a generic approach.
>>
>> Not following here. Please explain what this means.
>
> Let's say HSP/HFP. It doesn't make any sense to expose something like
> HandsfreeGateway1 or Headset1, because BlueZ has no knowledge about
> these profiles. They are implemented externally (i.e. oFono) and
> org.bluez.Profile1/ProfileManager1 obviously exposes a generic API.
> There's no way for example BlueZ can expose a property such as
> Headset1.IsPlaying, as BlueZ did in the past. This is an argument
> against expecting profile-specific interfaces in BlueZ.

Why would BlueZ expose these information anyway? I am still not following.

>>> Last but not least, I've found this interface (along with the test
>>> scripts) pretty useful while debugging issues reported recently in the
>>> mailing list. I believe this can be a fairly strong argument to
>>> support introducing an experimental interface.
>>
>> Could be also a debugging plugin or just a wakeup call that our unit and end-to-end tests are not good enough in this read.
>
> As argued above, I would discourage plugins exposing D-Bus API extensions.

We would apply the same rule as we did within oFono. org.bluez/org.ofono prefixed interface are generally useful. Specific interfaces can be expose by custom plugins. I have no problem with vendor interfaces if they think they are useful for certain specific users. If there is only one specific user, then making a custom plugin might be actually be better. It clearly indicates what its users are.

Regards

Marcel


2013-06-19 13:00:11

by Mikel Astiz

[permalink] [raw]
Subject: Re: [RFC BlueZ v2 00/13] Add experimental org.bluez.Service1

Hi Marcel,

On Wed, Jun 19, 2013 at 10:02 AM, Marcel Holtmann <[email protected]> wro=
te:
> Hi Mikel,
>
>>>> Beyond the desktop use-cases, some users (e.g. GENIVI) are interested =
in having profile-specific information and control interfaces exposed in D-=
Bus. Such APIs did exist in BlueZ 4 but were dropped for BlueZ 5 in favor o=
f simpler API simplicity. This service-specific interfaces had a fairly low=
priority for BlueZ 5 and therefore the discussion was postponed.
>>>>
>>>> This patchset proposes org.bluez.Service1 as an attempt to cover these=
needs. As compared to the former Device.ConnectProfile()/DisconnectProfile=
(), the approach has the following advantages:
>>>> - Multiple instances of the same UUID can be exposed.
>>>
>>> how is this going to help anyone. I you do not know what your service o=
bject is, what are you planning to do with it.
>>
>> See list below for a summary of real use-cases.
>>
>>>
>>>> - The state of each service can be exposed, without hackish lists like=
Device.ConnectedProfiles.
>>>
>>> Again, here I want to see clear example on why you want all these state=
s. Everybody takes about wanting all the crazy interim states and then neve=
r ever uses any of them.
>>
>> Many well-known UIs show this information (i.e. which profiles are
>> connected), including IVI examples or even the Android UI. A new
>> property such as "Device1.ConnectedProfiles" could be introduced but
>> see below for arguments against this approach.
>
> and many well-known UIs do not show this information. So what is the poin=
t exactly here. Maybe some UIs need to be brought into 2013 and being stuck=
in what we did 6 years ago.

This should be a UI decision IMO, not something BlueZ should impose
unless it's widely accepted.

>
> My question however was not about if a profile is connected or not, my qu=
estion was why we always end up with stupid interim states like "connecting=
".

I'm completely fine dropping the 'connecting' state, which I already
suggested to Johan.

>
>>>> - It's ObjectManager-centric.
>>>> - The design should scale better if new properties are required in the=
future (supported features, service handle, etc.)
>>>
>>> I don't think this actually will scale that nicely. There are limited s=
et of common properties for all these profiles. I am really curious to see =
how this is going to be used and how it would improve things for applicatio=
ns.
>>
>> The currently existing API hits its limits very soon, even if a
>> property such as "Device1.ConnectedProfiles" was introduced. Besides
>> the multiple-UUID-instances case, assume the following potential
>> scenarios:
>>
>> 1. That a user-setting (enabled/disabled) needs to be exposed per
>> service (or UUID), just like Android does. And I don't think you want
>> to have a Device1.EnabledProfiles property.
>
> I still do not agree with the fact that we need to disable profiles. Howe=
ver first order of business before I even consider allowing something like =
this is that we interop with ourselves on profiles (SDP record) updates and=
I have not seen anybody trying to handle this.

We're not talking about changing the SDP record here, just about
disconnecting profiles (and rejecting incoming connections). This is
what Android does and also our cars (generally speaking).

>
>> 2. That the 'connecting' state needs to be exposed in D-Bus. You'd
>> otherwise need something like Device1.ConnectingProfiles.
>
> My point is that we do not need the connecting state at all. What is it g=
ood for?

This is an advanced topic and as I said, I would be fine dropping this
right now.

>
>> 3. That a D-Bus clients needs to know if a UUID is connectable,
>> meaning not only supported but also probed (e.g. for "hfp", to
>> distinguish whether oFono is running). You'd otherwise need something
>> like Device1.ProbedProfiles.
>
> Has this really been thought through. What happens if a Device1.Connect i=
s triggered and then oFono starts later on. What is the expected behavior h=
ere?

Your questions needs to be answered regardless of org.bluez.Service1.
In a similar way, crashes in oFono could be handled by reconnecting
the profile automatically. I don't see this coupled in any way to this
proposal.

My point here was that the proposed D-Bus interface could express this
information nicely, in case the caller of ConnectProfile() knows it
will surely fail.

>
>> 4. That a human-friendly name needs to be exposed in D-Bus per service
>> (this seemed to be an argument in favor of the Network1 interface).
>
> And what name is that exactly? How do you handle translation?

A translation is also needed for profile-specific D-Bus interfaces.
You need to know that the UUID of PAN translates to
"org.bluez.Network1".

Having said this, I'm not particularly in favor of doing this. D-Bus
interfaces should be compact and without redundancies.

>
>> 5. That additional remote information needs to be exposed, such as
>> profile version or supported features.
>
> For what usage?

To show this in the UI, in some advanced tab, or as part of a debugging too=
l.

>
>> Having an object-oriented Service1 API is IMO much cleaner as opposed
>> to a sparse interface consisting of methods like
>> ConnectProfile()/DisconnectProfile() along with properties like
>> "ConnectedProfiles"/"ConnectingProfiles"/"EnabledProfiles"/"ProbedProfil=
es"
>> which btw are string lists, therefore not very client-friendly.
>>
>> All these needs would be nicely addressed by a generic service D-Bus
>> interface as proposed here. Note that some of the points above are
>> real IVI requirements, and we're in fact having trouble to upgrade to
>> BlueZ 5 due to API limitations. Some others might not be required
>> after all or might have different workarounds, but I think it's fair
>> enough to claim that the approach can scale better.
>
> If you need special interfaces, there is always the possibility of a vend=
or plugin using com.bmw or org.genivi interfaces.

Generally speaking, I don't think it's a good idea to encourage
plugins to expose custom D-Bus API extensions. Unless it's a very
specific feature which nobody else could ever benefit from, which I
don't think is the case.

Regarding genivi specifically, there's no way a consortium will pick
up an API that is not part of the official release.

Marking the interface as experimental already makes it non-stable and
non-default so I don't see why it should be so controversial to merge
it upstream. The code is fairly non-intrusive.

>
>>> My initial take is still that just calling a general Device1.Connect or=
requesting a connect to a UUID is useful. Everything else is pretty much a=
pplication specific anyway.
>>>
>>> You can always add profile specific interfaces. Like we do with Network=
1. Why are you saying your approach is more object manager centric.
>>
>> As argued above, it's presumably more object-oriented and therefore
>> more ObjectManager-centric.
>>
>> Adding interfaces such as Network1 is IMO a step back in the direction
>> to BlueZ 4 APIs, probably biased by the fact that the same developer
>> community is involved in Connman. Recent commits
>> (0624791ea6e917d6c9ecb8e7e6e5a1327199448d) mention that the
>> Connect/Disconnect interface was brought back "for convenience", which
>> should not be the driving factor when designing D-Bus APIs. This very
>> same argument was in fact used to drop "convenience" APIs such as
>> org.bluez.Manager (commit 86a7b07c22f3a595ba3c48092359287905bf0878).
>>
>> The Network1 interface could be easily deprecated in favor of a more
>> generic Service1 interface. If we're going to introduce interfaces
>> such as Headset1 etc., we should have stayed with BlueZ interfaces and
>> avoid breaking the API. A quick look at these old audio interfaces
>> (Headset, AudioSource, etc.) shows that Service1 could cover most of
>> them if not all.
>
> Network1 can not be deprecated for the simple reason that it is special. =
It returns a network interface. You saw my comments on why we left it in fo=
r BlueZ 5.

You're right, we couldn't completely remove the interface. You could
however deprecate the Connect()/Disconnect() methods because they are
redundant with ConnectProfile()/DisconnectProfile() or
org.bluez.Service1. There's already a property informing about the
interface name.

>
>> Furthermore, if you consider external profiles, it makes no sense to
>> expose a profile-specific interface in BlueZ, since there's no way
>> they could have profile-specific methods. org.bluez.Service1 fits
>> again nicely in this case as a generic approach.
>
> Not following here. Please explain what this means.

Let's say HSP/HFP. It doesn't make any sense to expose something like
HandsfreeGateway1 or Headset1, because BlueZ has no knowledge about
these profiles. They are implemented externally (i.e. oFono) and
org.bluez.Profile1/ProfileManager1 obviously exposes a generic API.
There's no way for example BlueZ can expose a property such as
Headset1.IsPlaying, as BlueZ did in the past. This is an argument
against expecting profile-specific interfaces in BlueZ.

>
>> Last but not least, I've found this interface (along with the test
>> scripts) pretty useful while debugging issues reported recently in the
>> mailing list. I believe this can be a fairly strong argument to
>> support introducing an experimental interface.
>
> Could be also a debugging plugin or just a wakeup call that our unit and =
end-to-end tests are not good enough in this read.

As argued above, I would discourage plugins exposing D-Bus API extensions.

Cheers,
Mikel

2013-06-19 08:02:13

by Marcel Holtmann

[permalink] [raw]
Subject: Re: [RFC BlueZ v2 00/13] Add experimental org.bluez.Service1

Hi Mikel,

>>> Beyond the desktop use-cases, some users (e.g. GENIVI) are interested in having profile-specific information and control interfaces exposed in D-Bus. Such APIs did exist in BlueZ 4 but were dropped for BlueZ 5 in favor of simpler API simplicity. This service-specific interfaces had a fairly low priority for BlueZ 5 and therefore the discussion was postponed.
>>>
>>> This patchset proposes org.bluez.Service1 as an attempt to cover these needs. As compared to the former Device.ConnectProfile()/DisconnectProfile(), the approach has the following advantages:
>>> - Multiple instances of the same UUID can be exposed.
>>
>> how is this going to help anyone. I you do not know what your service object is, what are you planning to do with it.
>
> See list below for a summary of real use-cases.
>
>>
>>> - The state of each service can be exposed, without hackish lists like Device.ConnectedProfiles.
>>
>> Again, here I want to see clear example on why you want all these states. Everybody takes about wanting all the crazy interim states and then never ever uses any of them.
>
> Many well-known UIs show this information (i.e. which profiles are
> connected), including IVI examples or even the Android UI. A new
> property such as "Device1.ConnectedProfiles" could be introduced but
> see below for arguments against this approach.

and many well-known UIs do not show this information. So what is the point exactly here. Maybe some UIs need to be brought into 2013 and being stuck in what we did 6 years ago.

My question however was not about if a profile is connected or not, my question was why we always end up with stupid interim states like "connecting".

>>> - It's ObjectManager-centric.
>>> - The design should scale better if new properties are required in the future (supported features, service handle, etc.)
>>
>> I don't think this actually will scale that nicely. There are limited set of common properties for all these profiles. I am really curious to see how this is going to be used and how it would improve things for applications.
>
> The currently existing API hits its limits very soon, even if a
> property such as "Device1.ConnectedProfiles" was introduced. Besides
> the multiple-UUID-instances case, assume the following potential
> scenarios:
>
> 1. That a user-setting (enabled/disabled) needs to be exposed per
> service (or UUID), just like Android does. And I don't think you want
> to have a Device1.EnabledProfiles property.

I still do not agree with the fact that we need to disable profiles. However first order of business before I even consider allowing something like this is that we interop with ourselves on profiles (SDP record) updates and I have not seen anybody trying to handle this.

> 2. That the 'connecting' state needs to be exposed in D-Bus. You'd
> otherwise need something like Device1.ConnectingProfiles.

My point is that we do not need the connecting state at all. What is it good for?

> 3. That a D-Bus clients needs to know if a UUID is connectable,
> meaning not only supported but also probed (e.g. for "hfp", to
> distinguish whether oFono is running). You'd otherwise need something
> like Device1.ProbedProfiles.

Has this really been thought through. What happens if a Device1.Connect is triggered and then oFono starts later on. What is the expected behavior here?

> 4. That a human-friendly name needs to be exposed in D-Bus per service
> (this seemed to be an argument in favor of the Network1 interface).

And what name is that exactly? How do you handle translation?

> 5. That additional remote information needs to be exposed, such as
> profile version or supported features.

For what usage?

> Having an object-oriented Service1 API is IMO much cleaner as opposed
> to a sparse interface consisting of methods like
> ConnectProfile()/DisconnectProfile() along with properties like
> "ConnectedProfiles"/"ConnectingProfiles"/"EnabledProfiles"/"ProbedProfiles"
> which btw are string lists, therefore not very client-friendly.
>
> All these needs would be nicely addressed by a generic service D-Bus
> interface as proposed here. Note that some of the points above are
> real IVI requirements, and we're in fact having trouble to upgrade to
> BlueZ 5 due to API limitations. Some others might not be required
> after all or might have different workarounds, but I think it's fair
> enough to claim that the approach can scale better.

If you need special interfaces, there is always the possibility of a vendor plugin using com.bmw or org.genivi interfaces.

>> My initial take is still that just calling a general Device1.Connect or requesting a connect to a UUID is useful. Everything else is pretty much application specific anyway.
>>
>> You can always add profile specific interfaces. Like we do with Network1. Why are you saying your approach is more object manager centric.
>
> As argued above, it's presumably more object-oriented and therefore
> more ObjectManager-centric.
>
> Adding interfaces such as Network1 is IMO a step back in the direction
> to BlueZ 4 APIs, probably biased by the fact that the same developer
> community is involved in Connman. Recent commits
> (0624791ea6e917d6c9ecb8e7e6e5a1327199448d) mention that the
> Connect/Disconnect interface was brought back "for convenience", which
> should not be the driving factor when designing D-Bus APIs. This very
> same argument was in fact used to drop "convenience" APIs such as
> org.bluez.Manager (commit 86a7b07c22f3a595ba3c48092359287905bf0878).
>
> The Network1 interface could be easily deprecated in favor of a more
> generic Service1 interface. If we're going to introduce interfaces
> such as Headset1 etc., we should have stayed with BlueZ interfaces and
> avoid breaking the API. A quick look at these old audio interfaces
> (Headset, AudioSource, etc.) shows that Service1 could cover most of
> them if not all.

Network1 can not be deprecated for the simple reason that it is special. It returns a network interface. You saw my comments on why we left it in for BlueZ 5.

> Furthermore, if you consider external profiles, it makes no sense to
> expose a profile-specific interface in BlueZ, since there's no way
> they could have profile-specific methods. org.bluez.Service1 fits
> again nicely in this case as a generic approach.

Not following here. Please explain what this means.

> Last but not least, I've found this interface (along with the test
> scripts) pretty useful while debugging issues reported recently in the
> mailing list. I believe this can be a fairly strong argument to
> support introducing an experimental interface.

Could be also a debugging plugin or just a wakeup call that our unit and end-to-end tests are not good enough in this read.

Regards

Marcel


2013-06-16 13:58:15

by Mikel Astiz

[permalink] [raw]
Subject: Re: [RFC BlueZ v2 00/13] Add experimental org.bluez.Service1

Hi Marcel,

On Sun, Jun 16, 2013 at 1:50 PM, Marcel Holtmann <[email protected]> wrot=
e:
> Hi Mikel,
>
>> Beyond the desktop use-cases, some users (e.g. GENIVI) are interested in=
having profile-specific information and control interfaces exposed in D-Bu=
s. Such APIs did exist in BlueZ 4 but were dropped for BlueZ 5 in favor of =
simpler API simplicity. This service-specific interfaces had a fairly low p=
riority for BlueZ 5 and therefore the discussion was postponed.
>>
>> This patchset proposes org.bluez.Service1 as an attempt to cover these n=
eeds. As compared to the former Device.ConnectProfile()/DisconnectProfile()=
, the approach has the following advantages:
>> - Multiple instances of the same UUID can be exposed.
>
> how is this going to help anyone. I you do not know what your service obj=
ect is, what are you planning to do with it.

See list below for a summary of real use-cases.

>
>> - The state of each service can be exposed, without hackish lists like D=
evice.ConnectedProfiles.
>
> Again, here I want to see clear example on why you want all these states.=
Everybody takes about wanting all the crazy interim states and then never =
ever uses any of them.

Many well-known UIs show this information (i.e. which profiles are
connected), including IVI examples or even the Android UI. A new
property such as "Device1.ConnectedProfiles" could be introduced but
see below for arguments against this approach.

>
>> - It's ObjectManager-centric.
>> - The design should scale better if new properties are required in the f=
uture (supported features, service handle, etc.)
>
> I don't think this actually will scale that nicely. There are limited set=
of common properties for all these profiles. I am really curious to see ho=
w this is going to be used and how it would improve things for applications=
.

The currently existing API hits its limits very soon, even if a
property such as "Device1.ConnectedProfiles" was introduced. Besides
the multiple-UUID-instances case, assume the following potential
scenarios:

1. That a user-setting (enabled/disabled) needs to be exposed per
service (or UUID), just like Android does. And I don't think you want
to have a Device1.EnabledProfiles property.
2. That the 'connecting' state needs to be exposed in D-Bus. You'd
otherwise need something like Device1.ConnectingProfiles.
3. That a D-Bus clients needs to know if a UUID is connectable,
meaning not only supported but also probed (e.g. for "hfp", to
distinguish whether oFono is running). You'd otherwise need something
like Device1.ProbedProfiles.
4. That a human-friendly name needs to be exposed in D-Bus per service
(this seemed to be an argument in favor of the Network1 interface).
5. That additional remote information needs to be exposed, such as
profile version or supported features.

Having an object-oriented Service1 API is IMO much cleaner as opposed
to a sparse interface consisting of methods like
ConnectProfile()/DisconnectProfile() along with properties like
"ConnectedProfiles"/"ConnectingProfiles"/"EnabledProfiles"/"ProbedProfiles"
which btw are string lists, therefore not very client-friendly.

All these needs would be nicely addressed by a generic service D-Bus
interface as proposed here. Note that some of the points above are
real IVI requirements, and we're in fact having trouble to upgrade to
BlueZ 5 due to API limitations. Some others might not be required
after all or might have different workarounds, but I think it's fair
enough to claim that the approach can scale better.

>
> My initial take is still that just calling a general Device1.Connect or r=
equesting a connect to a UUID is useful. Everything else is pretty much app=
lication specific anyway.
>
> You can always add profile specific interfaces. Like we do with Network1.=
Why are you saying your approach is more object manager centric.

As argued above, it's presumably more object-oriented and therefore
more ObjectManager-centric.

Adding interfaces such as Network1 is IMO a step back in the direction
to BlueZ 4 APIs, probably biased by the fact that the same developer
community is involved in Connman. Recent commits
(0624791ea6e917d6c9ecb8e7e6e5a1327199448d) mention that the
Connect/Disconnect interface was brought back "for convenience", which
should not be the driving factor when designing D-Bus APIs. This very
same argument was in fact used to drop "convenience" APIs such as
org.bluez.Manager (commit 86a7b07c22f3a595ba3c48092359287905bf0878).

The Network1 interface could be easily deprecated in favor of a more
generic Service1 interface. If we're going to introduce interfaces
such as Headset1 etc., we should have stayed with BlueZ interfaces and
avoid breaking the API. A quick look at these old audio interfaces
(Headset, AudioSource, etc.) shows that Service1 could cover most of
them if not all.

Furthermore, if you consider external profiles, it makes no sense to
expose a profile-specific interface in BlueZ, since there's no way
they could have profile-specific methods. org.bluez.Service1 fits
again nicely in this case as a generic approach.

Last but not least, I've found this interface (along with the test
scripts) pretty useful while debugging issues reported recently in the
mailing list. I believe this can be a fairly strong argument to
support introducing an experimental interface.

Cheers,
Mikel

2013-06-16 11:50:51

by Marcel Holtmann

[permalink] [raw]
Subject: Re: [RFC BlueZ v2 00/13] Add experimental org.bluez.Service1

Hi Mikel,

> Beyond the desktop use-cases, some users (e.g. GENIVI) are interested in having profile-specific information and control interfaces exposed in D-Bus. Such APIs did exist in BlueZ 4 but were dropped for BlueZ 5 in favor of simpler API simplicity. This service-specific interfaces had a fairly low priority for BlueZ 5 and therefore the discussion was postponed.
>
> This patchset proposes org.bluez.Service1 as an attempt to cover these needs. As compared to the former Device.ConnectProfile()/DisconnectProfile(), the approach has the following advantages:
> - Multiple instances of the same UUID can be exposed.

how is this going to help anyone. I you do not know what your service object is, what are you planning to do with it.

> - The state of each service can be exposed, without hackish lists like Device.ConnectedProfiles.

Again, here I want to see clear example on why you want all these states. Everybody takes about wanting all the crazy interim states and then never ever uses any of them.

> - It's ObjectManager-centric.
> - The design should scale better if new properties are required in the future (supported features, service handle, etc.)

I don't think this actually will scale that nicely. There are limited set of common properties for all these profiles. I am really curious to see how this is going to be used and how it would improve things for applications.

My initial take is still that just calling a general Device1.Connect or requesting a connect to a UUID is useful. Everything else is pretty much application specific anyway.

You can always add profile specific interfaces. Like we do with Network1. Why are you saying your approach is more object manager centric.

Regards

Marcel


2013-06-12 07:06:49

by Mikel Astiz

[permalink] [raw]
Subject: [RFC BlueZ v2 07/13] dbus: Add UUID property to org.bluez.Service1

From: Mikel Astiz <[email protected]>

Expose in D-Bus the remote UUID corresponding to each service instance.
---
src/service.c | 12 ++++++++++++
1 file changed, 12 insertions(+)

diff --git a/src/service.c b/src/service.c
index 3882b60..a04ff3f 100644
--- a/src/service.c
+++ b/src/service.c
@@ -363,8 +363,20 @@ static gboolean service_get_device(const GDBusPropertyTable *property,
return TRUE;
}

+static gboolean service_get_uuid(const GDBusPropertyTable *property,
+ DBusMessageIter *iter, void *data)
+{
+ struct btd_service *service = data;
+
+ dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING,
+ &service->profile->remote_uuid);
+
+ return TRUE;
+}
+
static const GDBusPropertyTable service_properties[] = {
{ "Device", "o", service_get_device },
+ { "UUID", "s", service_get_uuid },
{ }
};

--
1.8.1.4


2013-06-12 07:06:47

by Mikel Astiz

[permalink] [raw]
Subject: [RFC BlueZ v2 05/13] dbus: Add new org.bluez.Service1

From: Mikel Astiz <[email protected]>

Add a D-Bus interface to represent a service that is supported by a
device.
---
src/device.c | 8 ++++++--
src/service.c | 53 ++++++++++++++++++++++++++++++++++++++++++++++++++++-
src/service.h | 2 +-
3 files changed, 59 insertions(+), 4 deletions(-)

diff --git a/src/device.c b/src/device.c
index 5cb5460..d2be46b 100644
--- a/src/device.c
+++ b/src/device.c
@@ -177,6 +177,7 @@ struct btd_device {
GSList *uuids;
GSList *primaries; /* List of primary services */
GSList *services; /* List of btd_service */
+ unsigned int service_id;
GSList *pending; /* Pending services */
GSList *watches; /* List of disconnect_data */
gboolean temporary;
@@ -2135,6 +2136,7 @@ static struct btd_device *device_new(struct btd_adapter *adapter,

str2ba(address, &device->bdaddr);
device->adapter = adapter;
+ device->service_id = 1;

return btd_device_ref(device);
}
@@ -2480,11 +2482,12 @@ static void dev_probe(struct btd_profile *p, void *user_data)

service = service_create(d->dev, p);

- if (service_probe(service) < 0) {
+ if (service_probe(service, d->dev->service_id) < 0) {
btd_service_unref(service);
return;
}

+ d->dev->service_id++;
d->dev->services = g_slist_append(d->dev->services, service);
}

@@ -2502,11 +2505,12 @@ void device_probe_profile(gpointer a, gpointer b)

service = service_create(device, profile);

- if (service_probe(service) < 0) {
+ if (service_probe(service, device->service_id) < 0) {
btd_service_unref(service);
return;
}

+ device->service_id++;
device->services = g_slist_append(device->services, service);

if (!profile->auto_connect || !device->general_connect)
diff --git a/src/service.c b/src/service.c
index 83e1c1a..0c9477d 100644
--- a/src/service.c
+++ b/src/service.c
@@ -38,6 +38,7 @@
#include <bluetooth/bluetooth.h>

#include <glib.h>
+#include <gdbus/gdbus.h>

#include "log.h"

@@ -45,6 +46,10 @@
#include "device.h"
#include "profile.h"
#include "service.h"
+#include "dbus-common.h"
+#include "error.h"
+
+#define SERVICE_INTERFACE "org.bluez.Service1"

struct btd_service {
int ref;
@@ -53,6 +58,9 @@ struct btd_service {
void *user_data;
btd_service_state_t state;
int err;
+ char *path;
+ DBusMessage *connect_msg;
+ DBusMessage *disconnect_msg;
};

struct service_state_callback {
@@ -63,6 +71,9 @@ struct service_state_callback {

static GSList *state_callbacks = NULL;

+static int service_register(struct btd_service *service, unsigned int id);
+static void service_unregister(struct btd_service *service);
+
static const char *state2str(btd_service_state_t state)
{
switch (state) {
@@ -149,7 +160,7 @@ struct btd_service *service_create(struct btd_device *device,
return service;
}

-int service_probe(struct btd_service *service)
+int service_probe(struct btd_service *service, unsigned int id)
{
char addr[18];
int err;
@@ -159,6 +170,7 @@ int service_probe(struct btd_service *service)
err = service->profile->device_probe(service);
if (err == 0) {
change_state(service, BTD_SERVICE_STATE_DISCONNECTED, 0);
+ service_register(service, id); /* Ignore errors */
return 0;
}

@@ -170,6 +182,7 @@ int service_probe(struct btd_service *service)

void service_shutdown(struct btd_service *service)
{
+ service_unregister(service);
change_state(service, BTD_SERVICE_STATE_UNAVAILABLE, 0);
service->profile->device_remove(service);
service->device = NULL;
@@ -338,3 +351,41 @@ void btd_service_disconnecting_complete(struct btd_service *service, int err)
else /* If disconnect fails, we assume it remains connected */
change_state(service, BTD_SERVICE_STATE_CONNECTED, err);
}
+
+static int service_register(struct btd_service *service, unsigned int id)
+{
+ DBusConnection *dbus_conn = btd_get_dbus_connection();
+ char *path;
+
+ path = g_strdup_printf("%s/service%u", device_get_path(service->device),
+ id);
+ g_strdelimit(path, "-", '_');
+
+ if (g_dbus_register_interface(dbus_conn,
+ path, SERVICE_INTERFACE,
+ NULL, NULL, NULL, service,
+ NULL) == FALSE) {
+ g_free(path);
+ error("Unable to register service interface for %s",
+ service->path);
+ return -EIO;
+ }
+
+ service->path = path;
+
+ return 0;
+}
+
+static void service_unregister(struct btd_service *service)
+{
+ DBusConnection *dbus_conn = btd_get_dbus_connection();
+
+ if (service->path == NULL)
+ return;
+
+ g_dbus_unregister_interface(dbus_conn, service->path,
+ SERVICE_INTERFACE);
+
+ g_free(service->path);
+ service->path = NULL;
+}
diff --git a/src/service.h b/src/service.h
index 6ee8f17..e709f34 100644
--- a/src/service.h
+++ b/src/service.h
@@ -45,7 +45,7 @@ void btd_service_unref(struct btd_service *service);
struct btd_service *service_create(struct btd_device *device,
struct btd_profile *profile);

-int service_probe(struct btd_service *service);
+int service_probe(struct btd_service *service, unsigned int id);
void service_shutdown(struct btd_service *service);

/* Connection control API */
--
1.8.1.4


2013-06-12 07:06:50

by Mikel Astiz

[permalink] [raw]
Subject: [RFC BlueZ v2 08/13] dbus: Add state property to org.bluez.Service1

From: Mikel Astiz <[email protected]>

Add a property representing the current state of the service, which
currently uses a simplified subset of the internal state.
---
src/service.c | 37 +++++++++++++++++++++++++++++++++++++
1 file changed, 37 insertions(+)

diff --git a/src/service.c b/src/service.c
index a04ff3f..fd2ef79 100644
--- a/src/service.c
+++ b/src/service.c
@@ -92,10 +92,28 @@ static const char *state2str(btd_service_state_t state)
return NULL;
}

+static const char *state2dbus(btd_service_state_t state)
+{
+ switch (state) {
+ case BTD_SERVICE_STATE_UNAVAILABLE:
+ return NULL;
+ case BTD_SERVICE_STATE_DISCONNECTED:
+ case BTD_SERVICE_STATE_DISCONNECTING:
+ return "disconnected";
+ case BTD_SERVICE_STATE_CONNECTING:
+ return "connecting";
+ case BTD_SERVICE_STATE_CONNECTED:
+ return "connected";
+ }
+
+ return NULL;
+}
+
static void change_state(struct btd_service *service, btd_service_state_t state,
int err)
{
btd_service_state_t old = service->state;
+ DBusConnection *dbus_conn = btd_get_dbus_connection();
char addr[18];
GSList *l;

@@ -113,6 +131,11 @@ static void change_state(struct btd_service *service, btd_service_state_t state,
addr, service->profile->name,
state2str(old), state2str(state), err);

+ if (state != BTD_SERVICE_STATE_UNAVAILABLE &&
+ state2dbus(old) != state2dbus(state))
+ g_dbus_emit_property_changed(dbus_conn, service->path,
+ SERVICE_INTERFACE, "State");
+
for (l = state_callbacks; l != NULL; l = g_slist_next(l)) {
struct service_state_callback *cb = l->data;

@@ -374,9 +397,23 @@ static gboolean service_get_uuid(const GDBusPropertyTable *property,
return TRUE;
}

+static gboolean service_get_state(const GDBusPropertyTable *property,
+ DBusMessageIter *iter, void *data)
+{
+ struct btd_service *service = data;
+ const char *str;
+
+ str = state2dbus(service->state);
+
+ dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &str);
+
+ return TRUE;
+}
+
static const GDBusPropertyTable service_properties[] = {
{ "Device", "o", service_get_device },
{ "UUID", "s", service_get_uuid },
+ { "State", "s", service_get_state },
{ }
};

--
1.8.1.4


2013-06-12 07:06:54

by Mikel Astiz

[permalink] [raw]
Subject: [RFC BlueZ v2 12/13] test: Add test-service script

From: Mikel Astiz <[email protected]>

This simple script can be used to control the org.bluez.Service1 API.
---
test/test-service | 157 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 157 insertions(+)
create mode 100755 test/test-service

diff --git a/test/test-service b/test/test-service
new file mode 100755
index 0000000..48426f9
--- /dev/null
+++ b/test/test-service
@@ -0,0 +1,157 @@
+#!/usr/bin/python
+
+# Copyright (C) 2012-2013 BMW Car IT GmbH.
+
+from __future__ import absolute_import, print_function, unicode_literals
+
+from gi.repository import GObject
+
+import sys
+import dbus
+import dbus.mainloop.glib
+from optparse import OptionParser, make_option
+import bluezutils
+
+BUS_NAME = 'org.bluez'
+SERVICE_INTERFACE = 'org.bluez.Service1'
+
+dbus.mainloop.glib.DBusGMainLoop(set_as_default=True)
+bus = dbus.SystemBus()
+mainloop = GObject.MainLoop()
+
+option_list = [
+ make_option("-i", "--adapter", action="store",
+ type="string", dest="adap_id"),
+ ]
+
+description="Test script to operate on org.bluez.Service1 interfaces"
+usage = "usage: %prog [options] <command> [<args>]"
+epilog="""
+Commands:
+ list [<address>]
+ connect <address> <service-path-suffix>
+ disconnect <address> <service-path-suffix>
+
+"""
+
+OptionParser.format_epilog = lambda self, formatter: self.epilog
+parser = OptionParser(
+ usage=usage,
+ description = description,
+ epilog = epilog,
+ option_list=option_list)
+
+(options, args) = parser.parse_args()
+
+if len(args) < 1:
+ parser.print_help()
+ sys.exit(1)
+
+if args[0] == "list":
+ if len(args) > 2:
+ parser.print_help()
+ sys.exit(1)
+
+ adapter = bluezutils.find_adapter(options.adap_id)
+ adapter_path = adapter.object_path
+
+ om = dbus.Interface(bus.get_object("org.bluez", "/"),
+ "org.freedesktop.DBus.ObjectManager")
+ objects = om.GetManagedObjects()
+
+ all_services = (str(path) for path, interfaces in objects.iteritems()
+ if SERVICE_INTERFACE in interfaces.keys())
+ all_services = list(all_services)
+
+ for path, interfaces in objects.iteritems():
+ if "org.bluez.Device1" not in interfaces:
+ continue
+
+ properties = interfaces["org.bluez.Device1"]
+ if properties["Adapter"] != adapter_path:
+ continue
+
+ alias = properties["Alias"]
+ address = properties["Address"]
+ if len(args) >= 2 and args[1] != address:
+ continue
+
+ print("[ %s ('%s') ]" % (address, alias))
+
+ for service_path in all_services:
+ service = objects[service_path]
+ properties = service[SERVICE_INTERFACE]
+
+ if properties["Device"] != path:
+ continue
+
+ print(" [ " + service_path + " ]")
+
+ for key in properties.keys():
+ value = properties[key]
+ extra = ""
+ if key == "Device":
+ continue
+ elif key == "UUID":
+ alias = bluezutils.get_uuid_alias(value)
+ if alias:
+ extra = " (%s)" % alias
+ print(" %s = %s%s" % (key, value, extra))
+
+ print("")
+
+ sys.exit(0)
+
+def service_do(func):
+ if len(args) < 3:
+ parser.print_help()
+ sys.exit(1)
+
+ objects = bluezutils.get_managed_objects()
+ adapter = bluezutils.find_adapter_in_objects(objects, options.adap_id)
+ device = bluezutils.find_device_in_objects(objects, args[1],
+ options.adap_id)
+ path_suffix = args[2]
+ found = False
+ for path, ifaces in objects.iteritems():
+ service = ifaces.get(SERVICE_INTERFACE)
+ if service is None:
+ continue
+ if device.object_path != service["Device"]:
+ continue
+ if not(path.endswith(path_suffix)):
+ continue
+ try:
+ found = True
+ service = bus.get_object(BUS_NAME, path)
+ iface = dbus.Interface(service, SERVICE_INTERFACE)
+ func(path, iface)
+ except dbus.exceptions.DBusException as e:
+ print(e)
+ print()
+
+ if not(found):
+ raise Exception("Service not found")
+
+if args[0] == "connect":
+ def connect(path, iface):
+ print("Connecting service %s..." % path)
+ iface.Connect()
+ print("Done.")
+ print()
+
+ service_do(connect)
+ sys.exit(0)
+
+if args[0] == "disconnect":
+ def disconnect(path, iface):
+ print("Disconnecting service %s..." % path)
+ iface.Disconnect()
+ print("Done.")
+ print()
+
+ service_do(disconnect)
+ sys.exit(0)
+
+parser.print_help()
+sys.exit(1)
--
1.8.1.4


2013-06-12 07:06:48

by Mikel Astiz

[permalink] [raw]
Subject: [RFC BlueZ v2 06/13] dbus: Add Device property to org.bluez.Service1

From: Mikel Astiz <[email protected]>

Expose in D-Bus the object path of the device which the service belongs
to.
---
src/service.c | 18 +++++++++++++++++-
1 file changed, 17 insertions(+), 1 deletion(-)

diff --git a/src/service.c b/src/service.c
index 0c9477d..3882b60 100644
--- a/src/service.c
+++ b/src/service.c
@@ -352,6 +352,22 @@ void btd_service_disconnecting_complete(struct btd_service *service, int err)
change_state(service, BTD_SERVICE_STATE_CONNECTED, err);
}

+static gboolean service_get_device(const GDBusPropertyTable *property,
+ DBusMessageIter *iter, void *data)
+{
+ struct btd_service *service = data;
+ const char *path = device_get_path(service->device);
+
+ dbus_message_iter_append_basic(iter, DBUS_TYPE_OBJECT_PATH, &path);
+
+ return TRUE;
+}
+
+static const GDBusPropertyTable service_properties[] = {
+ { "Device", "o", service_get_device },
+ { }
+};
+
static int service_register(struct btd_service *service, unsigned int id)
{
DBusConnection *dbus_conn = btd_get_dbus_connection();
@@ -363,7 +379,7 @@ static int service_register(struct btd_service *service, unsigned int id)

if (g_dbus_register_interface(dbus_conn,
path, SERVICE_INTERFACE,
- NULL, NULL, NULL, service,
+ NULL, NULL, service_properties, service,
NULL) == FALSE) {
g_free(path);
error("Unable to register service interface for %s",
--
1.8.1.4


2013-06-12 07:06:55

by Mikel Astiz

[permalink] [raw]
Subject: [RFC BlueZ v2 13/13] test: Add --uuid to test-service

From: Mikel Astiz <[email protected]>

Extend the test script with an optional filter to let the user operate
on services matching a given UUID.
---
test/test-service | 34 ++++++++++++++++++++++++++--------
1 file changed, 26 insertions(+), 8 deletions(-)

diff --git a/test/test-service b/test/test-service
index 48426f9..f8dee66 100755
--- a/test/test-service
+++ b/test/test-service
@@ -22,6 +22,8 @@ mainloop = GObject.MainLoop()
option_list = [
make_option("-i", "--adapter", action="store",
type="string", dest="adap_id"),
+ make_option("-u", "--uuid", action="store",
+ type="string", dest="uuid"),
]

description="Test script to operate on org.bluez.Service1 interfaces"
@@ -29,8 +31,8 @@ usage = "usage: %prog [options] <command> [<args>]"
epilog="""
Commands:
list [<address>]
- connect <address> <service-path-suffix>
- disconnect <address> <service-path-suffix>
+ connect [<address> [<service-path-suffix>]]
+ disconnect [<address> [<service-path-suffix>]]

"""

@@ -47,6 +49,10 @@ if len(args) < 1:
parser.print_help()
sys.exit(1)

+uuid = None
+if options.uuid:
+ uuid = bluezutils.parse_uuid(options.uuid)
+
if args[0] == "list":
if len(args) > 2:
parser.print_help()
@@ -84,6 +90,10 @@ if args[0] == "list":

if properties["Device"] != path:
continue
+ elif uuid:
+ if properties["UUID"] != uuid:
+ continue
+ del(properties["UUID"])

print(" [ " + service_path + " ]")

@@ -103,23 +113,31 @@ if args[0] == "list":
sys.exit(0)

def service_do(func):
- if len(args) < 3:
- parser.print_help()
+ if len(args) < 3 and not(uuid):
+ print("ERROR: Either service suffix or UUID must be specified")
sys.exit(1)

objects = bluezutils.get_managed_objects()
adapter = bluezutils.find_adapter_in_objects(objects, options.adap_id)
- device = bluezutils.find_device_in_objects(objects, args[1],
+
+ device = None
+ if len(args) >= 2:
+ device = bluezutils.find_device_in_objects(objects, args[1],
options.adap_id)
- path_suffix = args[2]
+ path_suffix = None
+ if len(args) >= 3:
+ path_suffix = args[2]
+
found = False
for path, ifaces in objects.iteritems():
service = ifaces.get(SERVICE_INTERFACE)
if service is None:
continue
- if device.object_path != service["Device"]:
+ if device and device.object_path != service["Device"]:
+ continue
+ if uuid and uuid != service["UUID"]:
continue
- if not(path.endswith(path_suffix)):
+ if path_suffix and not(path.endswith(path_suffix)):
continue
try:
found = True
--
1.8.1.4


2013-06-12 07:06:53

by Mikel Astiz

[permalink] [raw]
Subject: [RFC BlueZ v2 11/13] dbus: Deprecate old profile-connecting API

From: Mikel Astiz <[email protected]>

The new org.bluez.Service1 interface deprecates the old UUID-specific
connect/disconnect methods in org.bluez.Device1.
---
doc/device-api.txt | 4 ++--
src/device.c | 6 ++++--
2 files changed, 6 insertions(+), 4 deletions(-)

diff --git a/doc/device-api.txt b/doc/device-api.txt
index 6201780..5329054 100644
--- a/doc/device-api.txt
+++ b/doc/device-api.txt
@@ -40,7 +40,7 @@ Methods void Connect()

Possible errors: org.bluez.Error.NotConnected

- void ConnectProfile(string uuid)
+ void ConnectProfile(string uuid) [Deprecated]

This method connects a specific profile of this
device. The UUID provided is the remote service
@@ -50,7 +50,7 @@ Methods void Connect()
org.bluez.Error.AlreadyConnected
org.bluez.Error.ConnectFailed

- void DisconnectProfile(string uuid)
+ void DisconnectProfile(string uuid) [Deprecated]

This method disconnects a specific profile of
this device. The profile needs to be registered
diff --git a/src/device.c b/src/device.c
index d2be46b..7c6f2c8 100644
--- a/src/device.c
+++ b/src/device.c
@@ -1728,9 +1728,11 @@ static DBusMessage *cancel_pairing(DBusConnection *conn, DBusMessage *msg,
static const GDBusMethodTable device_methods[] = {
{ GDBUS_ASYNC_METHOD("Disconnect", NULL, NULL, dev_disconnect) },
{ GDBUS_ASYNC_METHOD("Connect", NULL, NULL, dev_connect) },
- { GDBUS_ASYNC_METHOD("ConnectProfile", GDBUS_ARGS({ "UUID", "s" }),
+ { GDBUS_DEPRECATED_ASYNC_METHOD("ConnectProfile",
+ GDBUS_ARGS({ "UUID", "s" }),
NULL, connect_profile) },
- { GDBUS_ASYNC_METHOD("DisconnectProfile", GDBUS_ARGS({ "UUID", "s" }),
+ { GDBUS_DEPRECATED_ASYNC_METHOD("DisconnectProfile",
+ GDBUS_ARGS({ "UUID", "s" }),
NULL, disconnect_profile) },
{ GDBUS_ASYNC_METHOD("Pair", NULL, NULL, pair_device) },
{ GDBUS_METHOD("CancelPairing", NULL, NULL, cancel_pairing) },
--
1.8.1.4


2013-06-12 07:06:52

by Mikel Astiz

[permalink] [raw]
Subject: [RFC BlueZ v2 10/13] doc: Add API documentation for org.bluez.Service1

From: Mikel Astiz <[email protected]>

Update the documentation to reflect the newly adopted D-Bus API.
---
Makefile.am | 2 +-
doc/service-api.txt | 53 +++++++++++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 54 insertions(+), 1 deletion(-)
create mode 100644 doc/service-api.txt

diff --git a/Makefile.am b/Makefile.am
index 9d570fb..abab329 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -199,7 +199,7 @@ EXTRA_DIST += doc/assigned-numbers.txt doc/supported-features.txt

EXTRA_DIST += doc/mgmt-api.txt \
doc/adapter-api.txt doc/device-api.txt \
- doc/agent-api.txt doc/profile-api.txt \
+ doc/agent-api.txt doc/profile-api.txt doc/service-api.txt \
doc/network-api.txt doc/media-api.txt \
doc/health-api.txt doc/sap-api.txt

diff --git a/doc/service-api.txt b/doc/service-api.txt
new file mode 100644
index 0000000..e07eb75
--- /dev/null
+++ b/doc/service-api.txt
@@ -0,0 +1,53 @@
+BlueZ D-Bus Service API description
+***********************************
+
+Copyright (C) 2012-2013 BMW Car IT GmbH. All rights reserved.
+
+
+Service hierarchy
+=================
+
+Service unique name
+Interface org.bluez.Service1 [Experimental]
+Object path [variable prefix]/{hci0,hci1,...}/dev_XX_XX_XX_XX_XX_XX/
+ serviceZZZ
+
+Methods void Connect()
+
+ This method connects a specific service of this
+ device.
+
+ Possible errors: org.bluez.Error.AgentNotAvailable
+ org.bluez.Error.AlreadyConnected
+ org.bluez.Error.Canceled
+ org.bluez.Error.Failed
+ org.bluez.Error.InProgress
+
+ void Disconnect()
+
+ This method disconnects a specific service of this
+ device.
+
+ There is no connection tracking for a service, so
+ as long as the service is registered this will always
+ succeed.
+
+ Possible errors: org.bluez.Error.Failed
+ org.bluez.Error.InProgress
+ org.bluez.Error.NotConnected
+
+Properties object Device [readonly]
+
+ The object path of the device the service belongs to.
+
+ string State [readonly]
+
+ Indicates the state of the connection. Possible
+ values are:
+ "disconnected"
+ "connecting"
+ "connected"
+
+ string UUID [readonly]
+
+ 128-bit UUID that representing the remote service.
--
1.8.1.4


2013-06-12 07:06:51

by Mikel Astiz

[permalink] [raw]
Subject: [RFC BlueZ v2 09/13] dbus: Add Connect/Disconnect to org.bluez.Service1

From: Mikel Astiz <[email protected]>

Add the control methods to connect or disconnect a specific remote
service, in a similar way that org.bluez.Device1.ConnectProfile()/
.DisconnectProfile() do.
---
src/service.c | 81 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-
1 file changed, 80 insertions(+), 1 deletion(-)

diff --git a/src/service.c b/src/service.c
index fd2ef79..2bc45ac 100644
--- a/src/service.c
+++ b/src/service.c
@@ -109,6 +109,26 @@ static const char *state2dbus(btd_service_state_t state)
return NULL;
}

+static void send_dbus_reply(DBusMessage **p, bool state_ok, int err)
+{
+ DBusMessage *msg = *p;
+ DBusMessage *reply;
+
+ if (msg == NULL)
+ return;
+
+ if (state_ok)
+ reply = dbus_message_new_method_return(msg);
+ else if (err < 0)
+ reply = btd_error_failed(msg, strerror(-err));
+ else /* For some reason, the error was not set */
+ reply = btd_error_failed(msg, strerror(EIO));
+
+ g_dbus_send_message(btd_get_dbus_connection(), reply);
+ dbus_message_unref(msg);
+ *p = NULL;
+}
+
static void change_state(struct btd_service *service, btd_service_state_t state,
int err)
{
@@ -141,6 +161,12 @@ static void change_state(struct btd_service *service, btd_service_state_t state,

cb->cb(service, old, state, cb->user_data);
}
+
+ send_dbus_reply(&service->connect_msg,
+ state == BTD_SERVICE_STATE_CONNECTED, err);
+
+ send_dbus_reply(&service->disconnect_msg,
+ state == BTD_SERVICE_STATE_DISCONNECTED, err);
}

struct btd_service *btd_service_ref(struct btd_service *service)
@@ -397,6 +423,52 @@ static gboolean service_get_uuid(const GDBusPropertyTable *property,
return TRUE;
}

+static DBusMessage *service_connect(DBusConnection *conn, DBusMessage *msg,
+ void *user_data)
+{
+ struct btd_service *service = user_data;
+ int err;
+
+ if (service->state != BTD_SERVICE_STATE_DISCONNECTED)
+ return btd_error_already_connected(msg);
+
+ if (service->connect_msg != NULL || service->disconnect_msg)
+ return btd_error_busy(msg);
+
+ err = btd_service_connect(service);
+ if (err == -ENOTSUP)
+ return btd_error_not_supported(msg);
+ else if (err < 0)
+ return btd_error_failed(msg, strerror(-err));
+
+ service->connect_msg = dbus_message_ref(msg);
+
+ return NULL;
+}
+
+static DBusMessage *service_disconnect(DBusConnection *conn, DBusMessage *msg,
+ void *user_data)
+{
+ struct btd_service *service = user_data;
+ int err;
+
+ if (service->state == BTD_SERVICE_STATE_DISCONNECTED)
+ return btd_error_not_connected(msg);
+
+ if (service->disconnect_msg != NULL)
+ return btd_error_busy(msg);
+
+ err = btd_service_disconnect(service);
+ if (err == -ENOTSUP)
+ return btd_error_not_supported(msg);
+ else if (err < 0)
+ return btd_error_failed(msg, strerror(-err));
+
+ service->disconnect_msg = dbus_message_ref(msg);
+
+ return NULL;
+}
+
static gboolean service_get_state(const GDBusPropertyTable *property,
DBusMessageIter *iter, void *data)
{
@@ -410,6 +482,12 @@ static gboolean service_get_state(const GDBusPropertyTable *property,
return TRUE;
}

+static const GDBusMethodTable service_methods[] = {
+ { GDBUS_ASYNC_METHOD("Connect", NULL, NULL, service_connect) },
+ { GDBUS_ASYNC_METHOD("Disconnect", NULL, NULL, service_disconnect) },
+ { }
+};
+
static const GDBusPropertyTable service_properties[] = {
{ "Device", "o", service_get_device },
{ "UUID", "s", service_get_uuid },
@@ -428,7 +506,8 @@ static int service_register(struct btd_service *service, unsigned int id)

if (g_dbus_register_interface(dbus_conn,
path, SERVICE_INTERFACE,
- NULL, NULL, service_properties, service,
+ service_methods, NULL,
+ service_properties, service,
NULL) == FALSE) {
g_free(path);
error("Unable to register service interface for %s",
--
1.8.1.4


2013-06-12 07:06:46

by Mikel Astiz

[permalink] [raw]
Subject: [RFC BlueZ v2 04/13] test: Show human-friendly UUIDs in list-devices

From: Mikel Astiz <[email protected]>

Instead of showing the hexadecimal UUID, convert it to a human-friendly
alias as defined in the internal translation table in bluezutils.py.
---
test/list-devices | 6 +++++-
1 file changed, 5 insertions(+), 1 deletion(-)

diff --git a/test/list-devices b/test/list-devices
index 0aac217..57f9ba1 100755
--- a/test/list-devices
+++ b/test/list-devices
@@ -3,6 +3,7 @@
from __future__ import absolute_import, print_function, unicode_literals

import dbus
+import bluezutils

bus = dbus.SystemBus()

@@ -19,7 +20,10 @@ def extract_objects(object_list):
def extract_uuids(uuid_list):
list = ""
for uuid in uuid_list:
- if (uuid.endswith("-0000-1000-8000-00805f9b34fb")):
+ alias = bluezutils.get_uuid_alias(uuid)
+ if alias:
+ val = alias
+ elif (uuid.endswith("-0000-1000-8000-00805f9b34fb")):
if (uuid.startswith("0000")):
val = "0x" + uuid[4:8]
else:
--
1.8.1.4


2013-06-12 07:06:45

by Mikel Astiz

[permalink] [raw]
Subject: [RFC BlueZ v2 03/13] test: Support human-friendly UUIDs in test-device

From: Mikel Astiz <[email protected]>

Make use of the newly introduced helper function in bluezutils.py to
parse the command line UUID argument for "connect" and "disconnect"
operations.
---
test/test-device | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/test/test-device b/test/test-device
index 3d7b852..b5f8f96 100755
--- a/test/test-device
+++ b/test/test-device
@@ -99,7 +99,7 @@ if (args[0] == "connect"):
else:
device = bluezutils.find_device(args[1], options.dev_id)
if (len(args) > 2):
- device.ConnectProfile(args[2])
+ device.ConnectProfile(bluezutils.parse_uuid(args[2]))
else:
device.Connect()
sys.exit(0)
@@ -110,7 +110,7 @@ if (args[0] == "disconnect"):
else:
device = bluezutils.find_device(args[1], options.dev_id)
if (len(args) > 2):
- device.DisconnectProfile(args[2])
+ device.DisconnectProfile(bluezutils.parse_uuid(args[2]))
else:
device.Disconnect()
sys.exit(0)
--
1.8.1.4


2013-06-12 07:06:44

by Mikel Astiz

[permalink] [raw]
Subject: [RFC BlueZ v2 02/13] test: Add UUID alias table to bluezutils.py

From: Mikel Astiz <[email protected]>

Add a helper function to parse human-friendly strings that can be
translated into UUID strings. The input string can be one of the
following types:
- Full UUID: e.g. "00001108-0000-1000-8000-00805f9b34fb"
- Short UUID: e.g. "00001108"
- Alias: e.g. HSP_HS_UUID
- Short alias: e.g. HSP_HS
---
test/bluezutils.py | 84 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 84 insertions(+)

diff --git a/test/bluezutils.py b/test/bluezutils.py
index de08cbd..6fc55cb 100644
--- a/test/bluezutils.py
+++ b/test/bluezutils.py
@@ -3,6 +3,64 @@ import dbus
SERVICE_NAME = "org.bluez"
ADAPTER_INTERFACE = SERVICE_NAME + ".Adapter1"
DEVICE_INTERFACE = SERVICE_NAME + ".Device1"
+UUID_ALIAS_TABLE = {
+ "GENERIC_AUDIO_UUID": "00001203-0000-1000-8000-00805f9b34fb",
+ "HSP_HS_UUID": "00001108-0000-1000-8000-00805f9b34fb",
+ "HSP_AG_UUID": "00001112-0000-1000-8000-00805f9b34fb",
+ "HFP_HS_UUID": "0000111e-0000-1000-8000-00805f9b34fb",
+ "HFP_AG_UUID": "0000111f-0000-1000-8000-00805f9b34fb",
+ "ADVANCED_AUDIO_UUID": "0000110d-0000-1000-8000-00805f9b34fb",
+ "A2DP_SOURCE_UUID": "0000110a-0000-1000-8000-00805f9b34fb",
+ "A2DP_SINK_UUID": "0000110b-0000-1000-8000-00805f9b34fb",
+ "AVRCP_REMOTE_UUID": "0000110e-0000-1000-8000-00805f9b34fb",
+ "AVRCP_TARGET_UUID": "0000110c-0000-1000-8000-00805f9b34fb",
+ "PANU_UUID": "00001115-0000-1000-8000-00805f9b34fb",
+ "NAP_UUID": "00001116-0000-1000-8000-00805f9b34fb",
+ "GN_UUID": "00001117-0000-1000-8000-00805f9b34fb",
+ "BNEP_SVC_UUID": "0000000f-0000-1000-8000-00805f9b34fb",
+ "PNPID_UUID": "00002a50-0000-1000-8000-00805f9b34fb",
+ "DEVICE_INFORMATION_UUID": "0000180a-0000-1000-8000-00805f9b34fb",
+ "GATT_UUID": "00001801-0000-1000-8000-00805f9b34fb",
+ "IMMEDIATE_ALERT_UUID": "00001802-0000-1000-8000-00805f9b34fb",
+ "LINK_LOSS_UUID": "00001803-0000-1000-8000-00805f9b34fb",
+ "TX_POWER_UUID": "00001804-0000-1000-8000-00805f9b34fb",
+ "SAP_UUID": "0000112D-0000-1000-8000-00805f9b34fb",
+ "HEART_RATE_UUID": "0000180d-0000-1000-8000-00805f9b34fb",
+ "HEART_RATE_MEASUREMENT_UUID": "00002a37-0000-1000-8000-00805f9b34fb",
+ "BODY_SENSOR_LOCATION_UUID": "00002a38-0000-1000-8000-00805f9b34fb",
+ "HEART_RATE_CONTROL_POINT_UUID": "00002a39-0000-1000-8000-00805f9b34fb",
+ "HEALTH_THERMOMETER_UUID": "00001809-0000-1000-8000-00805f9b34fb",
+ "TEMPERATURE_MEASUREMENT_UUID": "00002a1c-0000-1000-8000-00805f9b34fb",
+ "TEMPERATURE_TYPE_UUID": "00002a1d-0000-1000-8000-00805f9b34fb",
+ "INTERMEDIATE_TEMPERATURE_UUID": "00002a1e-0000-1000-8000-00805f9b34fb",
+ "MEASUREMENT_INTERVAL_UUID": "00002a21-0000-1000-8000-00805f9b34fb",
+ "CYCLING_SC_UUID": "00001816-0000-1000-8000-00805f9b34fb",
+ "CSC_MEASUREMENT_UUID": "00002a5b-0000-1000-8000-00805f9b34fb",
+ "CSC_FEATURE_UUID": "00002a5c-0000-1000-8000-00805f9b34fb",
+ "SENSOR_LOCATION_UUID": "00002a5d-0000-1000-8000-00805f9b34fb",
+ "SC_CONTROL_POINT_UUID": "00002a55-0000-1000-8000-00805f9b34fb",
+ "RFCOMM_UUID_STR": "00000003-0000-1000-8000-00805f9b34fb",
+ "HDP_UUID": "00001400-0000-1000-8000-00805f9b34fb",
+ "HDP_SOURCE_UUID": "00001401-0000-1000-8000-00805f9b34fb",
+ "HDP_SINK_UUID": "00001402-0000-1000-8000-00805f9b34fb",
+ "HSP_HS_UUID": "00001108-0000-1000-8000-00805f9b34fb",
+ "HID_UUID": "00001124-0000-1000-8000-00805f9b34fb",
+ "DUN_GW_UUID": "00001103-0000-1000-8000-00805f9b34fb",
+ "GAP_UUID": "00001800-0000-1000-8000-00805f9b34fb",
+ "PNP_UUID": "00001200-0000-1000-8000-00805f9b34fb",
+ "SPP_UUID": "00001101-0000-1000-8000-00805f9b34fb",
+ "OBEX_SYNC_UUID": "00001104-0000-1000-8000-00805f9b34fb",
+ "OBEX_OPP_UUID": "00001105-0000-1000-8000-00805f9b34fb",
+ "OBEX_FTP_UUID": "00001106-0000-1000-8000-00805f9b34fb",
+ "OBEX_PCE_UUID": "0000112e-0000-1000-8000-00805f9b34fb",
+ "OBEX_PSE_UUID": "0000112f-0000-1000-8000-00805f9b34fb",
+ "OBEX_PBAP_UUID": "00001130-0000-1000-8000-00805f9b34fb",
+ "OBEX_MAS_UUID": "00001132-0000-1000-8000-00805f9b34fb",
+ "OBEX_MNS_UUID": "00001133-0000-1000-8000-00805f9b34fb",
+ "OBEX_MAP_UUID": "00001134-0000-1000-8000-00805f9b34fb"
+}
+
+INV_UUID_ALIAS_TABLE = { v:k for k, v in UUID_ALIAS_TABLE.items() }

def get_managed_objects():
bus = dbus.SystemBus()
@@ -45,3 +103,29 @@ def find_device_in_objects(objects, device_address, adapter_pattern=None):
return dbus.Interface(obj, DEVICE_INTERFACE)

raise Exception("Bluetooth device not found")
+
+def parse_uuid(s):
+ hex_digits = "0123456789abcdef"
+ uuid_digits = hex_digits + "-"
+ suffix = "-0000-1000-8000-00805f9b34fb"
+ short_len = 8
+ long_len = short_len + len(suffix)
+ us = s.upper()
+ ls = s.lower()
+
+ uuid = UUID_ALIAS_TABLE.get(us)
+ if uuid is None:
+ uuid = UUID_ALIAS_TABLE.get(us + "_UUID")
+ if not(uuid is None):
+ return uuid
+
+ if len(ls) == short_len and all((c in hex_digits) for c in ls):
+ return ls + suffix
+ elif len(ls) == long_len and all((c in uuid_digits) for c in ls):
+ return ls
+
+ raise Exception("Invalid profile UUID")
+
+def get_uuid_alias(uuid):
+ ul = uuid.lower()
+ return INV_UUID_ALIAS_TABLE.get(ul)
--
1.8.1.4


2013-06-12 07:06:43

by Mikel Astiz

[permalink] [raw]
Subject: [RFC BlueZ v2 01/13] test: Remove obsolete test script

From: Mikel Astiz <[email protected]>

The interface was removed some time ago making the test script useless,
so just remove it from the codebase.
---
Makefile.tools | 2 +-
test/simple-service | 128 ----------------------------------------------------
2 files changed, 1 insertion(+), 129 deletions(-)
delete mode 100755 test/simple-service

diff --git a/Makefile.tools b/Makefile.tools
index 8f245e0..d7fa17d 100644
--- a/Makefile.tools
+++ b/Makefile.tools
@@ -245,7 +245,7 @@ test_scripts += test/sap_client.py test/bluezutils.py \
test/dbusdef.py test/monitor-bluetooth test/list-devices \
test/test-discovery test/test-manager test/test-adapter \
test/test-device test/simple-agent \
- test/simple-service test/simple-endpoint test/test-sap-server \
+ test/simple-endpoint test/test-sap-server \
test/test-proximity test/test-network \
test/test-thermometer test/test-profile test/test-health \
test/test-health-sink test/service-record.dtd \
diff --git a/test/simple-service b/test/simple-service
deleted file mode 100755
index 02d7648..0000000
--- a/test/simple-service
+++ /dev/null
@@ -1,128 +0,0 @@
-#!/usr/bin/python
-
-from __future__ import absolute_import, print_function, unicode_literals
-
-import sys
-import time
-import dbus
-import bluezutils
-
-xml = ' \
-<?xml version="1.0" encoding="UTF-8" ?> \
-<record> \
- <attribute id="0x0001"> \
- <sequence> \
- <uuid value="0x1101"/> \
- </sequence> \
- </attribute> \
- \
- <attribute id="0x0002"> \
- <uint32 value="0"/> \
- </attribute> \
- \
- <attribute id="0x0003"> \
- <uuid value="00001101-0000-1000-8000-00805f9b34fb"/> \
- </attribute> \
- \
- <attribute id="0x0004"> \
- <sequence> \
- <sequence> \
- <uuid value="0x0100"/> \
- </sequence> \
- <sequence> \
- <uuid value="0x0003"/> \
- <uint8 value="23"/> \
- </sequence> \
- </sequence> \
- </attribute> \
- \
- <attribute id="0x0005"> \
- <sequence> \
- <uuid value="0x1002"/> \
- </sequence> \
- </attribute> \
- \
- <attribute id="0x0006"> \
- <sequence> \
- <uint16 value="0x656e"/> \
- <uint16 value="0x006a"/> \
- <uint16 value="0x0100"/> \
- </sequence> \
- </attribute> \
- \
- <attribute id="0x0007"> \
- <uint32 value="0"/> \
- </attribute> \
- \
- <attribute id="0x0008"> \
- <uint8 value="0xff"/> \
- </attribute> \
- \
- <attribute id="0x0009"> \
- <sequence> \
- <sequence> \
- <uuid value="0x1101"/> \
- <uint16 value="0x0100"/> \
- </sequence> \
- </sequence> \
- </attribute> \
- \
- <attribute id="0x000a"> \
- <url value="http://www.bluez.org/"/> \
- </attribute> \
- \
- <attribute id="0x000b"> \
- <url value="http://www.bluez.org/"/> \
- </attribute> \
- \
- <attribute id="0x000c"> \
- <url value="http://www.bluez.org/"/> \
- </attribute> \
- \
- <attribute id="0x0100"> \
- <text value="Serial Port"/> \
- </attribute> \
- \
- <attribute id="0x0101"> \
- <text value="Serial Port Service"/> \
- </attribute> \
- \
- <attribute id="0x0102"> \
- <text value="BlueZ"/> \
- </attribute> \
- \
- <attribute id="0x0200"> \
- <sequence> \
- <uint16 value="0x0100"/> \
- </sequence> \
- </attribute> \
- \
- <attribute id="0x0201"> \
- <uint32 value="0"/> \
- </attribute> \
-</record> \
-'
-
-bus = dbus.SystemBus()
-
-if len(sys.argv) > 1:
- path = bluezutils.find_adapter(sys.argv[1]).object_path
-else:
- path = bluezutils.find_adapter().object_path
-
-service = dbus.Interface(bus.get_object("org.bluez", path),
- "org.bluez.Service")
-
-handle = service.AddRecord(xml)
-
-print("Service record with handle 0x%04x added" % (handle))
-
-print("Press CTRL-C to remove service record")
-
-try:
- time.sleep(1000)
- print("Terminating session")
-except:
- pass
-
-service.RemoveRecord(dbus.UInt32(handle))
--
1.8.1.4


2013-07-04 08:27:26

by Mikel Astiz

[permalink] [raw]
Subject: Re: [RFC BlueZ v2 00/13] Add experimental org.bluez.Service1

Hi Marcel,

On Wed, Jun 19, 2013 at 11:13 PM, Marcel Holtmann <[email protected]> wro=
te:
> Hi Mikel,
>
>>>>>> Beyond the desktop use-cases, some users (e.g. GENIVI) are intereste=
d in having profile-specific information and control interfaces exposed in =
D-Bus. Such APIs did exist in BlueZ 4 but were dropped for BlueZ 5 in favor=
of simpler API simplicity. This service-specific interfaces had a fairly l=
ow priority for BlueZ 5 and therefore the discussion was postponed.
>>>>>>
>>>>>> This patchset proposes org.bluez.Service1 as an attempt to cover the=
se needs. As compared to the former Device.ConnectProfile()/DisconnectProfi=
le(), the approach has the following advantages:
>>>>>> - Multiple instances of the same UUID can be exposed.
>>>>>
>>>>> how is this going to help anyone. I you do not know what your service=
object is, what are you planning to do with it.
>>>>
>>>> See list below for a summary of real use-cases.
>>>>
>>>>>
>>>>>> - The state of each service can be exposed, without hackish lists li=
ke Device.ConnectedProfiles.
>>>>>
>>>>> Again, here I want to see clear example on why you want all these sta=
tes. Everybody takes about wanting all the crazy interim states and then ne=
ver ever uses any of them.
>>>>
>>>> Many well-known UIs show this information (i.e. which profiles are
>>>> connected), including IVI examples or even the Android UI. A new
>>>> property such as "Device1.ConnectedProfiles" could be introduced but
>>>> see below for arguments against this approach.
>>>
>>> and many well-known UIs do not show this information. So what is the po=
int exactly here. Maybe some UIs need to be brought into 2013 and being stu=
ck in what we did 6 years ago.
>>
>> This should be a UI decision IMO, not something BlueZ should impose
>> unless it's widely accepted.
>
> I am pretty much against displaying pointless information that nobody can=
use anyway. If my parents can not make sense out of it, they should not be=
there. If a non-tech person has no idea what all this crazy profile talk m=
eans, then it should not be there. This is my simplicity approach here.
>
> The goal should be to provide a solution that just works and not 100 knob=
s to tweak it around like a crazy monkey.
>
>>> My question however was not about if a profile is connected or not, my =
question was why we always end up with stupid interim states like "connecti=
ng".
>>
>> I'm completely fine dropping the 'connecting' state, which I already
>> suggested to Johan.
>>
>>>
>>>>>> - It's ObjectManager-centric.
>>>>>> - The design should scale better if new properties are required in t=
he future (supported features, service handle, etc.)
>>>>>
>>>>> I don't think this actually will scale that nicely. There are limited=
set of common properties for all these profiles. I am really curious to se=
e how this is going to be used and how it would improve things for applicat=
ions.
>>>>
>>>> The currently existing API hits its limits very soon, even if a
>>>> property such as "Device1.ConnectedProfiles" was introduced. Besides
>>>> the multiple-UUID-instances case, assume the following potential
>>>> scenarios:
>>>>
>>>> 1. That a user-setting (enabled/disabled) needs to be exposed per
>>>> service (or UUID), just like Android does. And I don't think you want
>>>> to have a Device1.EnabledProfiles property.
>>>
>>> I still do not agree with the fact that we need to disable profiles. Ho=
wever first order of business before I even consider allowing something lik=
e this is that we interop with ourselves on profiles (SDP record) updates a=
nd I have not seen anybody trying to handle this.
>>
>> We're not talking about changing the SDP record here, just about
>> disconnecting profiles (and rejecting incoming connections). This is
>> what Android does and also our cars (generally speaking).
>
> You brought up enabling and disabling profiles. And even if you just reje=
ct new connections, it is the same discussion. Actually having the record a=
nd the profile not available is even worse. There is also a attribute for s=
howing profile availability details.
>
> My major point is that we need to improve the way we deal with the situat=
ions when SDP databases change or single attributes of a record change. It =
seems something that everybody wants to swipe under the rug.
>
> What I am saying is that if you want to do anything like this in this are=
a, then there are 2 things that need to be done.
>
> a) We need to update BlueZ to handle this gracefully since we want to int=
erop with ourselves at least.
>
> b) Check what other major Bluetooth stack from major OSes are doing in th=
is area. And more important document their behavior. So how does iOS deal w=
ith PAN, PBAP and MAP rejections. Are they indicating them in the SDP recor=
d. How about Android, how about Windows, how about OS X.
>
> We do a lot of handwaving and wanting to add new APIs, but we do not have=
enough data to judge what is really needed as a configuration option and w=
hat can just be done automatically. The lazy way out by just adding a new p=
roperty or option is never something that I agree to likely. Feel free to p=
roof me wrong. You have all the tools in your hand to document the behavior=
of other stacks.
>
>>>> 2. That the 'connecting' state needs to be exposed in D-Bus. You'd
>>>> otherwise need something like Device1.ConnectingProfiles.
>>>
>>> My point is that we do not need the connecting state at all. What is it=
good for?
>>
>> This is an advanced topic and as I said, I would be fine dropping this
>> right now.
>>
>>>
>>>> 3. That a D-Bus clients needs to know if a UUID is connectable,
>>>> meaning not only supported but also probed (e.g. for "hfp", to
>>>> distinguish whether oFono is running). You'd otherwise need something
>>>> like Device1.ProbedProfiles.
>>>
>>> Has this really been thought through. What happens if a Device1.Connect=
is triggered and then oFono starts later on. What is the expected behavior=
here?
>>
>> Your questions needs to be answered regardless of org.bluez.Service1.
>> In a similar way, crashes in oFono could be handled by reconnecting
>> the profile automatically. I don't see this coupled in any way to this
>> proposal.
>>
>> My point here was that the proposed D-Bus interface could express this
>> information nicely, in case the caller of ConnectProfile() knows it
>> will surely fail.
>
> We never intended for ConnectProfile to be used that much. I assumed we j=
ust have Device1.Connect and then let BlueZ handle the rest. Individual pro=
file connections were just for profiles without auto-connect.
>
>>>> 4. That a human-friendly name needs to be exposed in D-Bus per service
>>>> (this seemed to be an argument in favor of the Network1 interface).
>>>
>>> And what name is that exactly? How do you handle translation?
>>
>> A translation is also needed for profile-specific D-Bus interfaces.
>> You need to know that the UUID of PAN translates to
>> "org.bluez.Network1".
>>
>> Having said this, I'm not particularly in favor of doing this. D-Bus
>> interfaces should be compact and without redundancies.
>
> Of course you need to know which UUID translate to which interface. Howev=
er that is not what I am talking about. You mention a human-friendly name. =
That name needs to be translatable if it represents something in a human la=
nguage.
>
>>>> 5. That additional remote information needs to be exposed, such as
>>>> profile version or supported features.
>>>
>>> For what usage?
>>
>> To show this in the UI, in some advanced tab, or as part of a debugging =
tool.
>
> The D-Bus API we have is not really a debug tool API. It is a main consum=
er UI.
>
> Why would be an advanced tab a good idea. Who would be using such an adva=
nced tab.
>
>>>> Having an object-oriented Service1 API is IMO much cleaner as opposed
>>>> to a sparse interface consisting of methods like
>>>> ConnectProfile()/DisconnectProfile() along with properties like
>>>> "ConnectedProfiles"/"ConnectingProfiles"/"EnabledProfiles"/"ProbedProf=
iles"
>>>> which btw are string lists, therefore not very client-friendly.
>>>>
>>>> All these needs would be nicely addressed by a generic service D-Bus
>>>> interface as proposed here. Note that some of the points above are
>>>> real IVI requirements, and we're in fact having trouble to upgrade to
>>>> BlueZ 5 due to API limitations. Some others might not be required
>>>> after all or might have different workarounds, but I think it's fair
>>>> enough to claim that the approach can scale better.
>>>
>>> If you need special interfaces, there is always the possibility of a ve=
ndor plugin using com.bmw or org.genivi interfaces.
>>
>> Generally speaking, I don't think it's a good idea to encourage
>> plugins to expose custom D-Bus API extensions. Unless it's a very
>> specific feature which nobody else could ever benefit from, which I
>> don't think is the case.
>>
>> Regarding genivi specifically, there's no way a consortium will pick
>> up an API that is not part of the official release.
>
> That is hilarious :)
>
>> Marking the interface as experimental already makes it non-stable and
>> non-default so I don't see why it should be so controversial to merge
>> it upstream. The code is fairly non-intrusive.
>
> It is not about how intrusive or non-intrusive the code is. The extra int=
erface must make sense for more than one user.
>
>>>>> My initial take is still that just calling a general Device1.Connect =
or requesting a connect to a UUID is useful. Everything else is pretty much=
application specific anyway.
>>>>>
>>>>> You can always add profile specific interfaces. Like we do with Netwo=
rk1. Why are you saying your approach is more object manager centric.
>>>>
>>>> As argued above, it's presumably more object-oriented and therefore
>>>> more ObjectManager-centric.
>>>>
>>>> Adding interfaces such as Network1 is IMO a step back in the direction
>>>> to BlueZ 4 APIs, probably biased by the fact that the same developer
>>>> community is involved in Connman. Recent commits
>>>> (0624791ea6e917d6c9ecb8e7e6e5a1327199448d) mention that the
>>>> Connect/Disconnect interface was brought back "for convenience", which
>>>> should not be the driving factor when designing D-Bus APIs. This very
>>>> same argument was in fact used to drop "convenience" APIs such as
>>>> org.bluez.Manager (commit 86a7b07c22f3a595ba3c48092359287905bf0878).
>>>>
>>>> The Network1 interface could be easily deprecated in favor of a more
>>>> generic Service1 interface. If we're going to introduce interfaces
>>>> such as Headset1 etc., we should have stayed with BlueZ interfaces and
>>>> avoid breaking the API. A quick look at these old audio interfaces
>>>> (Headset, AudioSource, etc.) shows that Service1 could cover most of
>>>> them if not all.
>>>
>>> Network1 can not be deprecated for the simple reason that it is special=
. It returns a network interface. You saw my comments on why we left it in =
for BlueZ 5.
>>
>> You're right, we couldn't completely remove the interface. You could
>> however deprecate the Connect()/Disconnect() methods because they are
>> redundant with ConnectProfile()/DisconnectProfile() or
>> org.bluez.Service1. There's already a property informing about the
>> interface name.
>
> We can not. The return the interface information in a race free way. Feel=
free to go back and read the discussion between Johan and I on this subjec=
t.
>
>>>> Furthermore, if you consider external profiles, it makes no sense to
>>>> expose a profile-specific interface in BlueZ, since there's no way
>>>> they could have profile-specific methods. org.bluez.Service1 fits
>>>> again nicely in this case as a generic approach.
>>>
>>> Not following here. Please explain what this means.
>>
>> Let's say HSP/HFP. It doesn't make any sense to expose something like
>> HandsfreeGateway1 or Headset1, because BlueZ has no knowledge about
>> these profiles. They are implemented externally (i.e. oFono) and
>> org.bluez.Profile1/ProfileManager1 obviously exposes a generic API.
>> There's no way for example BlueZ can expose a property such as
>> Headset1.IsPlaying, as BlueZ did in the past. This is an argument
>> against expecting profile-specific interfaces in BlueZ.
>
> Why would BlueZ expose these information anyway? I am still not following=
.
>
>>>> Last but not least, I've found this interface (along with the test
>>>> scripts) pretty useful while debugging issues reported recently in the
>>>> mailing list. I believe this can be a fairly strong argument to
>>>> support introducing an experimental interface.
>>>
>>> Could be also a debugging plugin or just a wakeup call that our unit an=
d end-to-end tests are not good enough in this read.
>>
>> As argued above, I would discourage plugins exposing D-Bus API extension=
s.
>
> We would apply the same rule as we did within oFono. org.bluez/org.ofono =
prefixed interface are generally useful. Specific interfaces can be expose =
by custom plugins. I have no problem with vendor interfaces if they think t=
hey are useful for certain specific users. If there is only one specific us=
er, then making a custom plugin might be actually be better. It clearly ind=
icates what its users are.

Is this a definitive nack to any approach involving a core interface?
Not even after dropping the 'connecting' state?

None of the arguments listed above against the approach seem very
strong to me, because this first version of the API is basically a
refactoring of an existing API, but I can understand your reluctance
to adding new interfaces if you have no interest in any of the
potential features it could eventually support.

Cheers,
Mikel