32feet BlueZ Test

# Table of Contents

1. Usage
2. General
3. General ToDo
4. Startup
5. BluetoothRadio
   1. Radio turned off
   2. Hardware unplugged
6. BluetoothDeviceInfo
   1. BluetoothDeviceInfo.GetServiceRecords
      1. General Records
      2. General -- All Records
7. BluetoothClient
   1. Connect
      1. Connect to 0x1105, with OBEX response received
      2. Connect to 0x1105, with no OBEX response received
      3. Connect to 0x1106, with no OBEX response received
8. NetworkStream
9. BluetoothListener
10. BluetoothSecurity
11. ObexWebRequest
12. ObexListener

# Usage

1. Support is now included in the main library and enabled by default.
2. Check whether `BluetoothRadio.PrimaryRadio` and/or `BluetoothRadio.AllRadios` are non-empty, and that one has `SoftwareManufacter` containing a BlueZ value.
3. Check what's in the trace.log file. It should contain something like the following.

       BlueZ XXXXXX
       Num factories: 1, Primary Factory: BlueZFactory

4. See below for what is and isn’t supported.

# General

This is a a summary see below for the details of each classes supported methods and properties.

1. BluetoothClient.Connect. On this stack Sockets are used for data transfer so that’s no a problem here. We haven’t looked at support for BluetoothClient.Authenticate, Encrypt , and SetPin.

2. BluetoothClient.DiscoverDevices. Currently discovers only devices within range. What's the API for getting “remembered” devices? No support for ‘live’ discovery.

3. BluetoothRadio. Support for Address, Name, and CoD. No support for Mode/Status and setting any values.

4. BluetoothDeviceInfo supports Address, and Name, and not the others including RSSI. There is good support for SDP querying.

5. There is partial support for BluetoothListener, we need to implement creating an SDP record -- first do the simple record case (SCL, PDL, SN).

6. We have done no work on BluetoothSecurity.

ObexWebRequest and ObexListener should work...

To check if BlueZ support is being loaded, check whether BluetoothRadio.PrimaryRadio or AllRadios returns a radio and whether its SoftwareManufacturer is a enum value of BlueZ.

Please let me have any feedback.

## Multi-stack

Don’t know if any other stacks can co-exist with BlueZ on Linux. Untested at least.

## Multi-radio

Unlike apparently every stack on Windows, BlueZ on Linux does supports multiple multiple radios. We don’t support this broadly currently. We will list the various active radios, and BluetoothClient and BluetoothListener can bind to a particular local address, however we don’t use a particular radio for device discovery nor for SDP querying.

# General TODO

(Test) To Do:

1. Odd threading, call Connect, Discovery, etc from all sorts of wierd threads.

Implementation To Do:

1. SetServiceState
2. Redo startup.

# Startup

To Do:

1. What if radio (dongle) is un-plugged?
2. What if radio is disabled? Is it automatically re-enabled, like Widcomm?

Test | Result
--- | ---
Radio (dongle) is un-plugged | Stack is not loaded
Radio is disabled | It does not turn-on automatically. Instead use Radio.set_Mode to enable it. Note we don't turn the radio on at any operation (as of r78137+).

# BluetoothRadio

To Do:

1. Mode 1) BothOff 2) BothOn 3) !Disco&NotConno
2. Change radio Name. 32feet sees?
3. Set Name; ex?
4. Set Mode; ex?
5. Radio turned off
6. Hardware unplugged
7. Double-check Off->Connectable transition.

See the two tables after this one for the values of the properties when the radio is disabled or un-plugged.

Test | Expected (seen by remote discovery) | Result
--- | --- | ---
ClassOfDevice ClassOfDevice { get; } | 0x4A0100, from Remote Discovery | 4A0100, device: Computer / service: Network, Capturing, Telephony
IntPtr Handle { get; } |  |
HardwareStatus HardwareStatus { get; } |  | ✘
HciVersion/Revision { get; } |  | v2_0wEdr, Subversion: 1958
Lmp[Sub]version { get; } |  | v2_0wEdr, Subversion: 1958
BluetoothAddress LocalAddress { get; } | 00:15:83:B4:1B:FA | 00:15:83:B4:1B:FA
Manufacturer Manufacturer { get; } |  | CambridgeSiliconRadio
RadioMode Mode { get; … | Connectable & Discoverable: No & Yes Yes & Yes No & No No & Yes!! | Unknown Results: n/a, n/a, n/a, n/a
set; } |  | NotImplemented
string Name { get; … | BT2.0 | BT2.0
set; } |  | ✘ NotImplemented
Manufacturer SoftwareManufacturer { get; } |  | BlueZXxxx

## Radio turned off

Property | Value
--- | ---
HardwareStatus | Shutdown
Mode | PowerOff
Name | empty (String.Empty)
Address | empty (BluetoothAddress.None)
Status of other properties is undefined.

## Hardware unplugged

Property | Value
--- | ---
HardwareStatus | NotPresent
Mode | PowerOff
Name | empty (String.Empty)
Address | empty (BluetoothAddress.None)
Status of other properties is undefined.

# BluetoothDeviceInfo

To Do:

1. DeviceName read, change name on remote device, check DeviceName is updated after a call to Refresh.
2. LastSeen.

To Do, for created-from:

1. By Discovery — Remembered.
2. By Discovery — Authenticated.
3. By Address — Get a Remembered device.
4. By Address — Get a Authenticated device.
5. By Address — Get a Non-existent device.
6. By Address — Get an Unknown but in-range Device.
7. By Discovery — DiscoOnly.
8. By Discovery — Unknown.
9. By Discovery — All devices.
10. By ‘Live’ Discovery — All devices.

Test Notes

1. May want to check Remembered list from Discovery at each step so see when that stack adds remembers devices.
2. Check timings of “by Address” cases.

Test | Result
--- | ---
bool Authenticated { get; } | ✘ returns false
ClassOfDevice ClassOfDevice { get; } | ✘ Is blank when new DeviceInfo etc. ✔ Valid when device is discovered by Inquiry.
bool Connected { get; } | ✘ returns false
BluetoothAddress DeviceAddress { get; } | ✔
string DeviceName { get; … | ✔
set; } | ✔ (Is not written to the system’s device database).
byte[][] GetServiceRecordsUnparsed(Guid service); | ✘ NotSupported
ServiceRecord[] GetServiceRecords(Guid service); | ✔
Guid[] InstalledServices { get; } | ✘ NotImplemented
DateTime LastSeen { get; } | ✘ Returns DateTime.MinValue.
DateTime LastUsed { get; } | ✘ Returns DateTime.MinValue.
void Refresh(); | ✔ Resets DeviceName alone.
bool Remembered { get; } | ✘ returns false
int Rssi { get; } | ✘ returns Int32.MinValue.
void SetServiceState(Guid service, bool state, bool throwOnError); | ✘ NotImplemented
void SetServiceState(Guid service, bool state);
void ShowDialog(); | ✘ NotImplemented
void Update(); | ✘ NotImplemented

#### Notes

1. `* MSFT+Win32 to Widcomm(000aaa6865bb):1101
   System.ComponentModel.Win32Exception: The specified service does not exist as an installed service
   at InTheHand.Net.Sockets.WindowsBluetoothDeviceInfo.SetServiceState(Guid service, Boolean state, Boolean throwOnError)
   *`

## BluetoothDeviceInfo.GetServiceRecords

To Do:

1. Query AllRecords (query class: L2CAP) with various remote records including one RFCOMM server running with a non-SIG-format service class id.
2. No matching records (e.g query class e.g. ReferencePrinting=0x1119)
3. Device not in range.
4. (Query for the SIG-format Class of a running remote service).
5. (Query for the UUID of Protocol Descr List e.g. BNEP (though L2CAP is like this).
6. Query for the non-SIG-format Class of a running remote service.
7. View a HID record.
8. View a PnP/DeviceInfo record.

Known flaws:

- Only services with standard Bluetooth Service Class Ids appear to be returned, those with custom UUIDs are not returned.
- There is no API to get general attributes. Instead there are a number of structs to return information about specific record type: SPP, HID and PnP/DeviceInfo. We get:
  - the Service Class Id, but only one value (some standard services have two);
  - we get the Service Name;
  - and the RFCOMM port number in most cases, see below.
  We also get some HID and PnP/DeviceInfo attributes.
- The SPP struct seems only to work work in certain cases, so we use a different API to try and get the RFCOMM port number. But again in some cases neither APIs succeed in fetching it. That's when querying Widcomm on WinXP at least...

### General Records

Test | Result | Error code on MSFT+Win32
--- | --- | ---
Query for a SIG-format Class of a running remote service | ✔
Query for no matching records (Use a UUID not present on the remote device, e.g. "ReferencePrinting 0x1119". Expect: See an empty result and not an exception). | ✔
Query for a custom (non-SIG-format) Class of a running remote service | ✘ Returns zero records.
Device not in range | ✔ | 10108 (WSASERVICE_NOT_FOUND)

### General -- All Records

Running SdpCreateTwoSppRecordsOneWithAvailAndBrowse e.g. on Widcomm, run SdpQuery/AllRecords on SdpBrowser BlueZ.

    ServiceName: 'Bluetooth Serial Port'
    ServiceClasses: 0x1101 SerialPort
    Channel Num: 1

    ServiceName: 'Network Access'
    ServiceClasses: 0x1117 GN
    Channel Num: <not assigned>

    ServiceName: 'Network Access'
    ServiceClasses: 0x1115 Panu
    Channel Num: <not assigned>

    ServiceName: 'Dial-up Networking'
    ServiceClasses: 0x1103 DialupNetworking
    Channel Num: 2

    ServiceName: 'File Transfer'
    ServiceClasses: 0x1106 ObexFileTransfer
    Channel Num: 4

    ServiceName: 'Fax'
    ServiceClasses: 0x1111 Fax
    Channel Num: 5

    ServiceName: 'PIM Synchronization'
    ServiceClasses: 0x1104 IrMCSync
    Channel Num: 6

    ServiceName: 'Sync Command Service'
    ServiceClasses: 0x1107 IrMCSyncCommand
    Channel Num: 6

    ServiceName: 'Headset'
    ServiceClasses: 0x1108 Headset, 0x1203 GenericAudio
    Channel Num: 7

    ServiceName: 'Headset'
    ServiceClasses: 0x111E Handsfree, 0x1203 GenericAudio
    Channel Num: 8

    ServiceName: 'Audio Gateway'
    ServiceClasses: 0x1112 HeadsetAudioGateway, 0x1203 GenericAudio
    Channel Num: 9

    ServiceName: <none>
    ServiceClasses: 0x1114 WapClient
    Channel Num: 22

    ServiceName: <none>
    ServiceClasses: e665c159-4721-4e73-8e97-9a8be42423d9
    >Channel Num: 10>
    ServiceName: 'Bluetooth Serial Port
    ServiceClasses: 0x1101 SerialPort
    Channel Num: 1

    ServiceName: 'Network Access
    ServiceClasses: 0x1117 GN
    Channel Num: <not assigned>

    ServiceName: 'Network Access
    ServiceClasses: 0x1115 Panu
    Channel Num: <not assigned>

    ServiceName: 'Dial-up Networking
    ServiceClasses: 0x1103 DialupNetworking
    Channel Num: 2

    ServiceName: 'File Transfer
    ServiceClasses: 0x1106 ObexFileTransfer
    Channel Num: 3

    ServiceName: 'Fax
    ServiceClasses: 0x1111 Fax
    Channel Num: 4

    ServiceName: 'PIM Synchronization
    ServiceClasses: 0x1104 IrMCSync
    Channel Num: 5

    ServiceName: 'Sync Command Service
    ServiceClasses: 0x1107 IrMCSyncCommand
    Channel Num: 5

    ServiceName: <none>
    ServiceClasses: 0x1114 WapClient
    Channel Num: 22

    ServiceName: <none>
    ServiceClasses: e665c159-4721-4e73-8e97-9a8be42423d9
    Channel Num: 6

Note: That's the result against Widcomm on WinXP. Against MSFT on WinXP the port for the WapClient service is correctly got.

# BluetoothClient

To Do:

1. xxxx
2. xxxx

| 32feet.NET
--- | ---
BluetoothDeviceInfo[] DiscoverDevices(…) | ✔ Including remembered and live.
BluetoothDeviceInfo[] Begin-/EndDiscoverDevices(…) | ✔ As DiscoverDevices.
TimeSpan InquiryLength { get; | ✐ Implemented, but need to re-test...
int InquiryAccessCode { get; set; } | ✘ Not used.
void Connect(…); | ✔
void Begin-/EndConnect(…); | ✔
bool Connected { get; } | ✔
NetworkStream GetStream(); | ✔
Socket Client { get; set; } | ✔
int Available { get; } | ✔
LingerOption LingerState { get; set; } | ✔
void Dispose(); | ✔ (ToDo disallow/abort device discovery ).
bool Authenticate { get; … | ✘ returns false
set; } | ✘ NotSupported
bool Encrypt { get; … | ✔ returns false
set; } | ✘ NotSupported
void SetPin(string pin); | ✘ NotImplemented
void SetPin(BluetoothAddress device, string pin); | ✘ NotImplemented
Guid LinkKey { get; } | ✘ NotImplemented
LinkPolicy LinkPolicy { get; } | ✘ NotImplemented
string GetRemoteMachineName(BluetoothAddress) | ✔
string RemoteMachineName { get; } | ✔
BluetoothEndPoint RemoteEndPoint { get; } | ✔

## Discovery

See BluetoothDeviceInfo for discovery testing.

To Do:

Test | Result | Error code on MSFT+Win32
--- | --- | ---

## Connect

To Do:

1. Connect to a specific port.
2. Service with non-SIG Class Id!
3. Radio disconnected
4. Radio/stack turned-off
5. (Service doesn't have RFCOMM pdl)
6. (No listener on port/channel specified in the Service Record)
7. ((Channel in record is invalid e.g. 100))
8. Connect to an OBEX service (OPP, and FTP), normal COM port connection created?
9. Connect to a other well known services (maybe HSP, PBAP, etc), normal COM port connection created?
10. Connect to server that has "require authentication" (and "require encryption"?).

Test | Result | Error code on MSFT+Win32
--- | --- | ---
Service with SIG Class Id e.g. Wap | ✔
Service with custom (non-SIG) Class Id (Use BtLsnr->ListSomeCustomUuids) | ✔
Connect to a particular port number | ✘ Need to re-test/investigate.
No Service with given Class Id | ✔ [NO_SERVICE] todo! ...... | ?10049
No Device with given Address | ✔ SoEx TimedOut [PAGE_TIMEOUT] | 10060 TimedOut
(Service doesn't have RFCOMM pdl) | ✔ [OPERATION_FAILURE] ! | 10064 HostDown
Already a connection to the RFCOMM port/channel | ✔ [NO_SERVICE] Re-test TODO | 10048 AddressAlreadyInUse
Connect to 0x1105 OBEX Push | ✘ Not supported by BlueSoleil
Connect to 0x1106 OBEX FTP | ✘ Not supported by BlueSoleil
Connect to server that has "require authentication" | ✐ [OPERATION_FAILURE] TODO Re-test

### Connect to 0x1105, with OBEX response received

`Connecting to: 008098244CA4:00001105-0000-1000-8000-00805f9b34fb:-1 ...
Gonna Btsdk_ConnectAppExtSPPService with: BtSdkAppExtSPPAttrStru sdp_record_handle: 0x0 service_class_128: 00001105-0000-1000-8000-00805f9b34fb svc_name: "\x00..." rf_svr_chnl: 0 com_index: 0.
HandleConnectionEventInd event: CONN_CFM, conn_hdl: 0x1C50FC8, arg.hDev: 0x1C60790
BlueSoleil LiveConns count: -2.
ret: OK, hConn: 0x1C50FC8, with: BtSdkAppExtSPPAttrStru sdp_record_handle: 0x0 service_class_128: 00001105-0000-1000-8000-00805f9b34fb svc_name: "\x00..." rf_svr_chnl: 0 com_index: 0.
Fail: sppAttr.rf_svr_chnl: 0
HandleConnectionEventInd event: DISC_CFM, conn_hdl: 0x1C50FC8, arg.hDev: 0x1C60790
InTheHand.Net.Bluetooth.BlueSoleil.BlueSoleilException: Exception of type 'InTheHand.Net.Bluetooth.BlueSoleil.BlueSoleilException' was thrown. Error: OBEX_INDEX (0x600)
Fail: RemoveConnection
BlueSoleil LiveConns count: 0.
`

### Connect to 0x1105, with no OBEX response received

`Gonna Btsdk_ConnectAppExtSPPService with: BtSdkAppExtSPPAttrStru sdp_record_handle: 0x0 service_class_128: 00001105-0000-1000-8000-00805f9b34fb svc_name: "\x00..." rf_svr_chnl: 0 com_index: 0.
ret: REQUEST_TIMEOUT, hConn: 0x0, with: BtSdkAppExtSPPAttrStru sdp_record_handle: 0x0 service_class_128: 00001105-0000-1000-8000-00805f9b34fb svc_name: "\x00..." rf_svr_chnl: 0 com_index: 0.
InTheHand.Net.Bluetooth.BlueSoleil.BlueSoleilException: Exception of type 'InTheHand.Net.Bluetooth.BlueSoleil.BlueSoleilException' was thrown. Error: REQUEST_TIMEOUT (0x6C8)`

### Connect to 0x1106, with no OBEX response received

`Gonna Btsdk_ConnectAppExtSPPService with: BtSdkAppExtSPPAttrStru sdp_record_handle: 0x0 service_class_128: 00001106-0000-1000-8000-00805f9b34fb svc_name: "\x00..." rf_svr_chnl: 0 com_index: 0.
ret: OPERATION_FAILURE, hConn: 0x0, with: BtSdkAppExtSPPAttrStru sdp_record_handle: 0x0 service_class_128: 00001106-0000-1000-8000-00805f9b34fb svc_name: "\x00..." rf_svr_chnl: 0 com_index: 0.
InTheHand.Net.Bluetooth.BlueSoleil.BlueSoleilException: Exception of type 'InTheHand.Net.Bluetooth.BlueSoleil.BlueSoleilException' was thrown. Error: OPERATION_FAILURE (0x302)`

# NetworkStream

To Do:

1. Read when buffer empty. On standard BlueSoleil (& System.IO.SerialPort) this fails...
2. Write an amount bigger than the buffer size. On standard BlueSoleil (& System.IO.SerialPort data is lost in this case...
3. Read after local close.
4. Write after local close.
5. Is connection closed automatically on app exit?
6. Is connection closed automatically on app crash GPF/etc?
7. Is connection closed automatically on Radio Off?
8. Is connection closed automatically on Radio Unplugged?
9. Read after remote close when there is still data in the buffer.
10. Pending Read through remote close when the remote sends data immediately before close.
11. Finalizable? Drop references to client and stream and check that the connection closes.
    Do we need to change the Add/CloseAnyLiveConnections to not store a strong reference.

Test | Expected | Result
--- | --- | ---
Read when buffer empty. | On standard BlueSoleil (& System.IO.SerialPort) this fails... | ✔
Write an amount bigger than the buffer size. | On standard BlueSoleil (& System.IO.SerialPort) data is lost in this case... | ✔
Read thru/after Remote Close | Return zero. | ✔
Write thru(if poss)/after Remote Close | IOEx(SocketEx(WSAECONNABORTED)). | ✔
Flush when not-open | NetworkStream does not throw ObjectDisposedException. | ✔
Client.Connected after local close |  | ✔
Client.Connected after network close | Should be false after the connection close and there's been a failing I/O operation. | ✔ We have Connected=false as *soon* as the connection closes rather than only after an I/O operation.
Connections close at app exit? | BlueSoleil doesn't automatically handle this. Oh, how I wish they and Widcomm used real Windows Handles, and not their own ids. (Maybe it’s by design however, so that you can keep using the connection through its COM port after the creating application exits). | ✔ We use a CriticalFinalizerObject to close each open connection.
Connections close at Radio Off? | BlueSoleil does NOT automatically handle this. Writes do fail, but Reads block waiting for data. | ✔
Connections close at Radio Unplugged? | BlueSoleil DOES automatically handle this. | ✔
Read thru Local Close | “System.IO.IOException: Unable to read data from the transport connection: A blocking operation was interrupted by a call to WSACancelBlockingCall. ---> System.Net.Sockets.SocketException: A blocking operation was interrupted by a call to WSACancelBlockingCall” | ✔
Read after Local Close | “System.ObjectDisposedException: Cannot access a disposed object. Object name: 'System.Net.Sockets.NetworkStream'.” | ✔
Write after Local Close | ObjectDisposedEx. | ✔
Read after remote close when there is still data in the buffer. |  | ✘ Doesn't return the data, only returns zero-length!
Pending Read through remote close when the remote sends data immediately before close. | Get the data then zero-return. | ✔
xxxx | yyyy | ✔ / ✘ zzzz

# BluetoothListener

Not Supported. There seems to be no BlueSoleil API for server-side SPP/RFCOMM.

# BluetoothSecurity

To Do:

1. RemoveDevice
2. RemoveDevice full remove?
3. Test/implement PairRequest

Test | Expected | Result
--- | --- | ---
PairRequest(BluetoothAddress device, string pin) |  |
RemoveDevice(BluetoothAddress device) |  | ✔ Does full remove: name and auth'd state etc are gone.
SetPin(BluetoothAddress device, string pin) |  | ✘ NotImplemented
RevokePin(BluetoothAddress device) |  | ✘ NotImplemented
BluetoothAddress GetPinRequest() |  | ✘ NotImplemented. Is it supportable??
RefusePinRequest(BluetoothAddress device) |  | ✘ NotImplemented. Is it supportable??
SetLinkKey(BluetoothAddress a, Guid linkkey) |  | ✘ NotImplemented. Is it supportable??

# ObexWebRequest

Not supported currently. Our default implementation doesn’t work because it uses a RFCOMM connection, and as noted above BlueSoleil doesn’t support such connections to don’t work to known Profiles. There might be a BlueSoleil OBEX API we could use instead. Volunteers welcomed.

# ObexListener

Not supported currently. Our default implementation doesn’t work because as noted above there’s no BlueSoleil API for server-side SPP/RFCOMM. There might be a BlueSoleil OBEX-server API we could use instead. Volunteers welcomed.

- end of document -