iocp package reference (V1.1.0)

# ::iocp::bt::sdrTop, Main, Index

The `iocp::bt::sdr` namespace contains commands for parsing Bluetooth Service Discovery Records (SDR) as returned by the ::iocp::bt::device services and ::iocp::bt::device service_references commands. These return a list of binary records which must be first parsed with the decode command. Individual service attributes can then be retrieved from the decoded records using the other commands in the namespace.

    set dev  [iocp::bt::device address "APN Phone"]
    set recs [iocp::bt::device services $dev]
    set sdr  [iocp::bt::sdr::decode [lindex $recs 0]]
    set service_classes [iocp::bt::sdr::attribute get $sdr ServiceClassIDList]

## Commandssdr, Top, Main, Index

#### attribute exists [::iocp::bt::sdr]sdr, Top, Main, Index

Checks if an attribute exists in a service discovery record

attribute exists sdr attr_id ?varname?

##### Parameters

`sdr` | A decoded service discovery record in the form returned by decode.
`attr_id` | Attribute integer id or Bluetooth universal attribute name.
`varname` | If not the empty string, the attribute value is stored in a variable of this name in the caller's context. Optional, default `""`.

##### Description

The value returned in `$varname` is the attribute type-independent data value. To get the value formatted for the attribute type, use the attribute get command.

##### Return value

Returns 1 if the attribute exists, and 0 otherwise.

Show source

    proc ::iocp::bt::sdr::attribute::exists {sdr attr_id {varname {}}} {

        # Checks if an attribute exists in a service discovery record
        # sdr - a decoded service discovery record in the form returned by
        #       [decode].
        # attr_id - attribute integer id or Bluetooth universal attribute name
        # varname - If not the empty string, the attribute
        #           value is stored in a variable of this name in the
        #           caller's context.
        #
        # The value returned in $varname is the attribute type-independent data
        # value. To get the value formatted for the attribute type, use the
        # [attribute get] command.
        #
        # Returns 1 if the attribute exists, and 0 otherwise.

        if {[string is integer -strict $attr_id]} {
            set key [expr {$attr_id + 0}]; # Force decimal rep. Faster than format
        } else {
            set key [names::attribute_id $attr_id]; # name -> id
        }

        if {[dict exists $sdr $key]} {
            if {$varname ne ""} {
                upvar 1 $varname value
                set value [dict get $sdr $key]
            }
            return 1
        }

        return 0
    }
    # NOTE: showing source of procedure implementing ensemble subcommand.

#### attribute get [::iocp::bt::sdr]sdr, Top, Main, Index

Get the value of an universal attribute from a service discovery record.

attribute get sdr attr_id ?varname?

##### Parameters

`sdr` | Decoded service discovery record.
`attr_id` | Universal attribute name or numeric identifier.
`varname` | Optional name of variable in caller's context. Optional, default `""`.

##### Description

If `$varname` is not the empty string, it should be the name of a variable in the caller's context.

- If the attribute is present, the command returns `1` and stores the attribute value in the variable.
- If the attribute is not present, the command returns 0.

If `$varname` is an empty string,

- If the attribute is present, the command returns its value.
- If the attribute is not present, the command raises an error.

The attribute value is decoded into a attribute type-dependent format.

The `$attr_id` argument must be one of those defined in the Universal Attributes section in the Bluetooth Assigned Numbers specification. Otherwise an error is raised. The recognized attributes are listed below. Refer to the Bluetooth specification for their exact semantics.

AdditionalProtocolDescriptorList | List of protocol stacks. This supplements the `ProtocolDescriptorList` attribute.
BluetoothProfileDescriptorList | List of Bluetooth profile descriptors to which the service conforms. Each element is a dictionary with keys `Uuid`, `Name`, `MajorVersion` and `MinorVersion` corresponding to the referenced profile.
BrowseGroupList | List of browse group descriptors representing the browse groups to which the service record belongs. Each descriptor is dictionary with keys `Uuid` and `Name` identifying the a browse group.
ClientExecutableURL | URL pointing to the client application that may be used to utilize the service.
DocumentationURL | URL for the service documentation.
IconURL | URL for icon that may be used to represent the service.
LanguageBaseAttributeIDList | Nested dictionary of language-specific attribute offsets (see below).
ProtocolDescriptorList | List of protocol stacks (see below) that may be used to access the service described by the service record.
ProviderName | Name of entity providing the service in the primary language of the service record.
ServiceAvailability | A value in the range 0-255 that indicates relative availability of the service in terms of the number of clients it can accept. Note this is a scaled estimate.
ServiceClassIDList | List with each element being a dictionary corresponding to a service class that the record conforms to. The keys of the dictionary are `Name` and `Uuid`.
ServiceDescription | A description of the service in the primary language of the record.
ServiceID | UUID that uniquely identifies a service instance.
ServiceInfoTimeToLive | The number of seconds from the time it was generated that the service record information is expected to be valid.
ServiceName | The human-readable name of the service in the primary language of the record.
ServiceRecordHandle | A numeric value that uniquely identifies a service record within a SDP server.
ServiceRecordState | Numeric value that is changed any time the service record is modified by the SDP server.

In the case of ProtocolDescriptorList and AdditionalProtocolDescriptorList, the attribute value is in the form of a list each element of which describes a protocol stack that may be used to access the service. Each such element is itself a list whose elements correspond to layers in that protocol stack starting with the lowest layer first. Each layer is described as a dictionary with the following keys:

Uuid | The UUID of the protocol for the layer.
Name | The name of the protocol.
Params | The protocol parameters. This is a list of raw data elements of the form `{type value}` the interpretation of which is protocol dependent.

In the case of LanguageBaseAttributeIDList, the attribute value is a dictionary indexed by language identifiers as defined in ISO639, e.g. `en`, `fr` etc. The corresponding value is itself a dictionary with keys `BaseOffset` and `Encoding`. See the Bluetooth specification for the former. The latter is either a Tcl encoding name or in case the encoding is not supported by Tcl, an integer value that identifies an encoding as per the Bluetooth specification.

##### Return value

Returns a boolean or the decoded attribute value.

Show source

    proc ::iocp::bt::sdr::attribute::get {sdr attr_id {varname {}}} {

        # Get the value of an universal attribute from a service
        # discovery record.
        #  sdr     - Decoded service discovery record.
        #  attr_id - Universal attribute name or numeric identifier.
        #  varname - Optional name of variable in caller's context.
        #
        # If $varname is not the empty string, it should be the name of
        # a variable in the caller's context.
        #  - If the attribute is present, the command returns `1` and
        #    stores the attribute value in the variable.
        #  - If the attribute is not present, the command returns 0.
        #
        # If $varname is an empty string,
        #  - If the attribute is present, the command returns its value.
        #  - If the attribute is not present, the command raises an error.
        #
        # The attribute value is decoded into a attribute type-dependent format.
        #
        # The $attr_id argument must be one of those defined in
        # the *Universal Attributes* section in the
        # [Bluetooth Assigned Numbers](https://www.bluetooth.com/specifications/assigned-numbers/service-discovery/)
        # specification. Otherwise an error is raised. The recognized attributes
        # are listed below. Refer to the Bluetooth specification for their
        # exact semantics.
        #
        # AdditionalProtocolDescriptorList - List of protocol stacks. This
        #    supplements the `ProtocolDescriptorList` attribute.
        # BluetoothProfileDescriptorList - List of Bluetooth profile descriptors
        #    to which the service conforms. Each element is a dictionary
        #    with keys `Uuid`, `Name`, `MajorVersion` and `MinorVersion`
        #    corresponding to the referenced profile.
        # BrowseGroupList - List of browse group descriptors representing
        #    the browse groups to which the service record belongs. Each
        #    descriptor is dictionary with keys `Uuid` and `Name` identifying
        #    the a browse group.
        # ClientExecutableURL - URL pointing to the client application that
        #    may be used to utilize the service.
        # DocumentationURL - URL for the service documentation.
        # IconURL - URL for icon that may be used to represent the service.
        # LanguageBaseAttributeIDList - Nested dictionary of language-specific
        #    attribute offsets (see below).
        # ProtocolDescriptorList - List of protocol stacks (see below) that may
        #    be used to access the service described by the service record.
        # ProviderName - Name of entity providing the service in the primary
        #    language of the service record.
        # ServiceAvailability - A value in the range 0-255 that indicates
        #    relative availability of the service in terms of the number of
        #    clients it can accept. Note this is a scaled estimate.
        # ServiceClassIDList - List with each element being a dictionary
        #    corresponding to a service class that the record conforms to.
        #    The keys of the dictionary are `Name` and `Uuid`.
        # ServiceDescription - A description of the service in the primary
        #    language of the record.
        # ServiceID - UUID that uniquely identifies a service instance.
        # ServiceInfoTimeToLive - The number of seconds from the time it was
        #    generated that the service record information is expected to be valid.
        # ServiceName - The human-readable name of the service in the primary
        #    language of the record.
        # ServiceRecordHandle - A numeric value that uniquely identifies a service
        #    record within a SDP server.
        # ServiceRecordState - Numeric value that is changed any time the service
        #    record is modified by the SDP server.
        #
        # In the case of ProtocolDescriptorList and AdditionalProtocolDescriptorList,
        # the attribute value is in the form of a list each element of
        # which describes a protocol stack that may be used to access the
        # service. Each such element is itself a list whose elements correspond
        # to layers in that protocol stack starting with the lowest layer first.
        # Each layer is described as a dictionary with the following keys:
        #  Uuid - the UUID of the protocol for the layer
        #  Name - the name of the protocol
        #  Params - the protocol parameters. This is a list of raw data elements
        #           of the form `{type value}` the interpretation of which is
        #           protocol dependent.
        #
        # In the case of LanguageBaseAttributeIDList, the attribute value is
        # a dictionary indexed by language
        # identifiers as defined in ISO639, e.g. `en`, `fr` etc.
        # The corresponding value is itself a dictionary with keys `BaseOffset`
        # and `Encoding`. See the Bluetooth specification for the former.
        # The latter is either a Tcl encoding name or in case the encoding
        # is not supported by Tcl, an integer value that identifies an encoding
        # as per the Bluetooth specification.
        #
        # Returns a boolean or the decoded attribute value.

        set attr_name [names::attribute_name $attr_id]
        set attr_id   [names::attribute_id $attr_id]
        switch -exact -- $attr_name {
            BrowseGroupList -
            ServiceClassIDList {
                tailcall Uuids $sdr $attr_id $varname
            }
            BluetoothProfileDescriptorList -
            ProtocolDescriptorList -
            AdditionalProtocolDescriptorList -
            LanguageBaseAttributeIDList {
                tailcall $attr_name $sdr $varname
            }
            DocumentationURL -
            ClientExecutableURL -
            IconURL -
            ServiceAvailability -
            ServiceInfoTimeToLive -
            ServiceID -
            ServiceRecordState -
            ServiceRecordHandle {
                tailcall AttributeValue $sdr $attr_id $varname
            }
            ServiceName -
            ServiceDescription -
            ProviderName {
                # Special case because they need a language argument
                tailcall text $sdr $attr_name primary $varname
            }
            default {
                if {[llength [info commands [namespace current]::universal::$attr_name]]} {
                    tailcall [namespace current]::universal::$attr_name $sdr $varname
                }
            }
        }
        error "Unknown universal attribute \"$attr_id\"."
    }
    # NOTE: showing source of procedure implementing ensemble subcommand.

#### attribute raw [::iocp::bt::sdr]sdr, Top, Main, Index

Get an attribute value from an service discovery record.

attribute raw sdr attr_id

##### Parameters

`sdr` | A decoded service discovery record in the form returned by sdr_decode.
`attr_id` | Attribute integer id.

##### Description

The command will raise an error if the attribute does not exist in the sdr.

##### Return value

Returns the attribute value as a pair consisting of the type and the raw data element value.

Show source

    proc ::iocp::bt::sdr::attribute::raw {sdr attr_id} {

        # Get an attribute value from an service discovery record.
        # sdr - a decoded service discovery record in the form returned by
        #       sdr_decode.
        # attr_id   - attribute integer id
        #
        # The command will raise an error if the attribute does not exist
        # in the sdr.
        #
        # Returns the attribute value as a pair consisting of the type and
        # the raw data element value.

        if {[exists $sdr $attr_id value]} {
            return $value
        } else {
            error "Attribute with id \"$attr_id\" not found."
        }
    }
    # NOTE: showing source of procedure implementing ensemble subcommand.

#### attribute text [::iocp::bt::sdr]sdr, Top, Main, Index

Get the value of an text attribute in the specified language from a service discovery record.

attribute text sdr attr_id lang ?varname?

##### Parameters

`sdr` | Decoded service discovery record.
`attr_id` | Universal attribute name or numeric identifier.
`lang` | Language identifier as specified in iso639, e.g. `en` for english, `fr` for french etc. or the keyword `primary`.
`varname` | Name of variable in caller's context. Optional, default `""`.

##### Description

If `$varname` is not the empty string, it should be the name of a variable in the caller's context.

- If the attribute is present, the command returns `1` and stores the attribute value in the variable.
- If the attribute is not present, the command returns 0.

If `$varname` is an empty string,

- If the attribute is present, the command returns its value.
- If the attribute is not present, the command raises an error.

If the record does not contain a value for the specified language, the value for the primary language will be retrieved.

##### Return value

Returns a boolean or the attribute text value.

Show source

    proc ::iocp::bt::sdr::attribute::text {sdr attr_id lang {varname {}}} {

        # Get the value of an text attribute in the specified language
        # from a service discovery record.
        #  sdr     - Decoded service discovery record.
        #  attr_id - Universal attribute name or numeric identifier.
        #  lang - language identifier as specified in iso639, e.g. `en` for
        #         english, `fr` for french etc. or the keyword `primary`.
        #  varname - Name of variable in caller's context.
        #
        # If $varname is not the empty string, it should be the name of
        # a variable in the caller's context.
        #  - If the attribute is present, the command returns `1` and
        #    stores the attribute value in the variable.
        #  - If the attribute is not present, the command returns 0.
        #
        # If $varname is an empty string,
        #  - If the attribute is present, the command returns its value.
        #  - If the attribute is not present, the command raises an error.
        #
        # If the record does not contain a value for the specified language,
        # the value for the primary language will be retrieved.
        #
        # Returns a boolean or the attribute text value.
        #
        set lang_offset [names::attribute_id $attr_id]
        if {$lang_offset < 256 || $lang_offset >= 512} {
            error "Invalid text attribute id \"$attr_id\"."
        }
        incr lang_offset -256
        tailcall TextAttribute $sdr $lang_offset $lang $varname
    }
    # NOTE: showing source of procedure implementing ensemble subcommand.

#### attributes [::iocp::bt::sdr]sdr, Top, Main, Index

Get the list of attributes in a service discovery record

attributes sdr

##### Parameters

`sdr` | A decoded service discovery record in the form returned by decode.

##### Return value

Returns a list of numeric attribute ids.

Show source

    proc ::iocp::bt::sdr::attributes {sdr} {

        # Get the list of attributes in a service discovery record
        # sdr - a decoded service discovery record in the form returned by
        #       [decode].
        # Returns a list of numeric attribute ids.
        return [dict keys $sdr]
    }

#### decode [::iocp::bt::sdr]sdr, Top, Main, Index

Decodes a binary service discovery record

decode binsdr

##### Parameters

`binsdr` | A raw Bluetooth service discovery record in binary form.

##### Description

The returned value should be treated as opaque. The attributes stored in the record should be accessed with the commands in the `sdr` namespace.

##### Return value

Returns a container of attributes stored in the record.

Show source

    proc ::iocp::bt::sdr::decode {binsdr} {

        # Decodes a binary service discovery record
        # binsdr - a raw Bluetooth service discovery record in binary
        #       form.
        #
        # The returned value should be treated as opaque. The attributes
        # stored in the record should be accessed with the commands in
        # the `sdr` namespace.
        #
        # Returns a container of attributes stored in the record.

        # A SDR record is a single data element which is a sequence
        # containing nested data elements.
        # {{sequence {nested data elements}}}
        set rec [lindex [DecodeElements $binsdr] 0 1]

        # $rec contains alternating attribute value pairs. The result
        # is built as a list as its faster to do that and shimmer
        # to a dict on access than to build a dictionary to begin with.
        set sdr {}
        foreach {attr val} $rec {
            lappend sdr [lindex $attr 1] $val
        }
        return $sdr
    }

#### print [::iocp::bt::sdr]sdr, Top, Main, Index

Prints a SDP record to a more human readable form.

print rec ?attrfilter?

##### Parameters

`rec` | A binary SDP record in the form returned by ::iocp::bt::device services or ::iocp::bt::device service_references.
`attrfilter` | If specified, only attribute names matching the filter using `string match` are printed. Optional, default `*`.

Show source

    proc ::iocp::bt::sdr::print {rec {attrfilter *}} {

        # Prints a SDP record to a more human readable form.
        # rec - a binary SDP record in the form returned by [::iocp::bt::device services] or
        #       [::iocp::bt::device service_references].
        # attrfilter - If specified, only attribute names matching the filter
        #       using `string match` are printed.
        #

        # Alternating attribute value pairs
        set rec [decode $rec]
        foreach attr [attributes $rec] {
            set attrname [names::attribute_name $attr]
            if {[string is integer -strict $attrname]} {
                # For easier matching with Bluetooth specs
                set attrname [format 0x%x $attrname]
            }
            if {![string match -nocase $attrfilter $attrname]} {
                continue
            }
            switch -exact -- $attrname {
                ServiceID -
                ServiceRecordState -
                ServiceInfoTimeToLive -
                ServiceAvailability -
                DocumentationURL -
                ClientExecutableURL -
                IconURL -
                ServiceName -
                ServiceDescription -
                ProviderName -
                ServiceRecordHandle {
                    puts "$attrname: [attribute get $rec $attrname]"
                }
                ServiceClassIDList {
                    puts "$attrname: sequence"
                    foreach elem [attribute get $rec $attrname] {
                        dict with elem {
                            # We use this for printing service-specific attributes
                            lappend service_class_uuids $Uuid
                            if {$Name eq $Uuid} {
                                puts "    $Uuid"
                            } else {
                                puts "    $Uuid $Name"
                            }
                        }
                    }
                }
                ProtocolDescriptorList {
                    puts "$attrname:"
                    foreach protocol [attribute get $rec $attrname] {
                        puts "    ProtocolStack:"
                        foreach layer $protocol {
                            set name [dict get $layer ProtocolName]
                            set uuid [dict get $layer Protocol]
                            set params [lmap param [dict get $layer ProtocolParams] {
                                PrintableElement $param
                            }]
                            if {$name eq ""} {
                                puts "        $uuid ([join $params {, }])"
                            } else {
                                puts "        $uuid $name ([join $params {, }])"
                            }
                        }
                    }
                }
                AdditionalProtocolDescriptorList {
                    # Like ProtocolDescriptorList but an additional level
                    # of nesting.
                    puts "$attrname:"
                    foreach additional_protocol [attribute get $rec $attrname] {
                        foreach protocol $additional_protocol {
                            puts "    ProtocolStack:"
                            foreach layer $protocol {
                                set name [dict get $layer ProtocolName]
                                set uuid [dict get $layer Protocol]
                                set params [lmap param [dict get $layer ProtocolParams] {
                                    PrintableElement $param
                                }]
                                if {$name eq ""} {
                                    puts "        $uuid ([join $params {, }])"
                                } else {
                                    puts "        $uuid $name ([join $params {, }])"
                                }
                            }
                        }
                    }
                }
                BrowseGroupList {
                    puts "$attrname: sequence"
                    foreach elem [attribute get $rec $attrname] {
                        dict with elem {
                            if {$Name eq $Uuid} {
                                puts "    $Uuid"
                            } else {
                                puts "    $Uuid $Name"
                            }
                        }
                    }
                }
                LanguageBaseAttributeIDList {
                    puts "$attrname: sequence"
                    dict for {lang val} [attribute get $rec $attrname] {
                        puts "    $lang: [dict get $val Encoding], [dict get $val BaseOffset]"
                    }
                }
                BluetoothProfileDescriptorList {
                    puts "$attrname: sequence"
                    foreach profile [attribute get $rec $attrname] {
                        # Sequence of profiles. Each profile is a sequence of
                        # profile uuid and version.
                        # puts profile:$profile
                        dict with profile {
                            if {$Uuid eq $Name} {
                                puts "    $Uuid v$MajorVersion.$MinorVersion"
                            } else {
                                puts "    $Uuid $Name v$MajorVersion.$MinorVersion"
                            }
                        }
                    }
                }
                default {
                    if {$attr >= 0x100 && $attr <= 0x1ff} {
                        # TBD - special handling for 0x100-0x1ff attributes by
                        # looking up languages table
                        puts "$attrname: [PrintableElement [attribute raw $rec $attr]]"
                    } else {
                        lassign [PrintableServiceSpecificAttribute  $service_class_uuids $rec $attr] attrname attrval
                        puts "$attrname: $attrval"
                    }
                }
            }
        }
    }

#### printn [::iocp::bt::sdr]sdr, Top, Main, Index

Prints a SDP record to a more human readable form.

printn recs ?attrfilter?

##### Parameters

`recs` | A list of binary SDP records in the form returned by ::iocp::bt::device services or ::iocp::bt::device service_references.
`attrfilter` | If specified, only attribute names matching the filter using `string match` are printed. Optional, default `*`.

Show source

    proc ::iocp::bt::sdr::printn {recs {attrfilter *}} {

        # Prints a SDP record to a more human readable form.
        # recs - a list of binary SDP records in the form returned by
        #        [::iocp::bt::device services] or [::iocp::bt::device service_references].
        # attrfilter - If specified, only attribute names matching the filter
        #       using `string match` are printed.
        #
        set sep ""
        foreach rec $recs {
            puts $sep
            set sep "--------------------------------------------"
            print $rec $attrfilter
        }
    }

# Start page

# Index

# iocp::bt

# iocp::bt::names

# iocp::bt::sdr

# iocp::inet

# iocp::bt::sdr

## Commands

#### attribute exists Checks if an attribute exists in a service discovery record

#### attribute get Get the value of an universal attribute from a service discovery record.

#### attribute raw Get an attribute value from an service discovery record.

#### attribute text Get the value of an text attribute in the specified language from a service discovery record.

#### attributes Get the list of attributes in a service discovery record

#### decode Decodes a binary service discovery record

#### print Prints a SDP record to a more human readable form.

#### printn Prints a SDP record to a more human readable form.

Document generated by Ruff!