# Bluetooth Bluecove searchService findet nichts



## SuperPCFan (25. Mrz 2021)

Hallo Forum,

ich versuche einen Bluetooth Client mit der Bluecove Bibliothek zum Laufen zu bekommen, um darüber später ein HID-Gerät und ein Dateiaustausch zu ermöglichen. Unten angehängt ist meine Testklasse. Um das Verhalten von Bluetooth besser nachvollziehen zu können, sind dort bewusst noch blocking- Thread.sleep enthalten. Das Multithreading kommt später.

Mein Problem ist, dass ich mit "searchServices()" bei Test's mit unterschiedlichen Bluetooth Geräten keinen einzigen Service finde. Es ist gut möglich, dass ich das Bluetooth-Konzept aus den ganzen Informationen im Internet missinterpretiert habe. Mein grundsätzliches Verständnis des späteren Ablaufes (der GUI-Teil ist in dem Code unten nicht enthalten und ist durch Konstanten ersetzt) ist aktuell:
Geräte in der nähe suchen
Geräte in der nähe anzeigen
 -> User wählt Gerät für Verbindung
Gewähltes Gerät nach dem von mir benötigten Service fragen
-> wenn Service nicht verfügbar -> User-Meldung: Gerät ungeeignet
-> wenn Service verfügbar -> Server/Service URL aus dem gefundenen Services holen und Verbindung öffnen

Nur finde ich für die von mir versuchten Service-Ids "0011", "1124" oder "1106" keine Services. Eines meiner Testgeräte ist auch ein Win-PC der mindestens den HID Service unterstützen müsste. Aber selbst das konnte ich bisher nicht verifizieren, da ich bisher keine Möglichkeit gefunden habe ein bestimmtes Gerät einfach nach "allen verfügbaren" Services zu fragen. In der API sehe ich nur Service-such-Befehle um nach bestimmten/bekannten Services zu fragen.

Entweder missverstehe ich die Befehle oder die Channel/SDP Id's. Hat jemand einen Tipp?

[CODE lang="java" title="Bluecove Testklasse"]import java.io.IOException;
import java.util.Arrays;
import java.util.Queue;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.bluetooth.*;
import javax.microedition.io.Connector;
import javax.microedition.io.StreamConnection;

public class BtHidDataHandler implements DiscoveryListener {
    private final BtHidDataHandler btHandler;
    private Logger log;

    //private final UUID hidServiceId = new UUID("1124", true);
    private final UUID hidServiceId = new UUID("0011", true);
    private final UUID ftpServiceId = new UUID("1106", true);
    private final UUID[] serviceIdTable = new UUID[]{hidServiceId};
    //private final UUID[] serviceIdTable = new UUID[]{hidServiceId, ftpServiceId};

    private final Queue<RemoteDevice> foundRemoteDevices = new ConcurrentLinkedQueue<RemoteDevice>();
    private final Queue<ServiceRecord> foundRemoteServices = new ConcurrentLinkedQueue<ServiceRecord>();

    public void startDeviceAccessTest(){

        try{
            // ja, is der stöpsel denn auch an? 
            if (LocalDevice.isPowerOn()){

                // lokaler zugriff
                LocalDevice ld = LocalDevice.getLocalDevice();
                String sLocalName = ld.getFriendlyName();
                log.log(Level.INFO, "mein Gerät heißt: {0}", sLocalName);

                // bekannte partner finden
                DiscoveryAgent agent = ld.getDiscoveryAgent();
                RemoteDevice[] knownPairingPartners = agent.retrieveDevices(DiscoveryAgent.PREKNOWN);
                if (knownPairingPartners != null) {
                    log.log(Level.INFO, "Meine Freunde heißen: ");
                    for(RemoteDevice rd : knownPairingPartners){
                        log.log(Level.INFO, "{0} : {1}", new Object[]{rd.getFriendlyName(false), rd.getBluetoothAddress()});
                    }
                }

                // starte device suche
                foundRemoteDevices.clear();
                agent.startInquiry(DiscoveryAgent.GIAC, btHandler);
                log.log(Level.INFO, "Gerätesuche gestartet");
                try {Thread.sleep(15000);} catch (InterruptedException ex) {}
                log.log(Level.INFO, "Gerätesuche warten beendet");

                // finde meinen partner
                RemoteDevice myRD = null;
                log.log(Level.INFO, "Geräte gefunden: ");
                while(!foundRemoteDevices.isEmpty()){
                    RemoteDevice device = foundRemoteDevices.poll();
                    log.log(Level.INFO, "{0} : {1}", new Object[]{device.getFriendlyName(false), device.getBluetoothAddress()});
                    if (device.getFriendlyName(false).equals("Galaxy M30s")){
                        myRD = device;
                    }
                }

                // partner steht zur verfügung
                if (myRD != null){
                    // starte service suche
                    foundRemoteServices.clear();
                    agent.searchServices(null, serviceIdTable, myRD, this);
                    log.log(Level.INFO, "Servicesuche gestartet");
                    try {Thread.sleep(15000);} catch (InterruptedException ex) {}
                    log.log(Level.INFO, "Servicesuche warten beendet");

                    // wurde überhaupt ein service gefunden?
                    if (!foundRemoteServices.isEmpty()) {


                        // ermittle aus dem ersten gefundenen service die server url
                        log.log(Level.INFO, "Services gefunden: ");
                        String url = null;
                        while(!foundRemoteServices.isEmpty()){
                            ServiceRecord record = foundRemoteServices.poll();
                            log.log(Level.INFO, "url: {0}", record.getConnectionURL(ServiceRecord.NOAUTHENTICATE_NOENCRYPT, false));
                            if (url == null) {
                                url = record.getConnectionURL(ServiceRecord.AUTHENTICATE_ENCRYPT, false);
                            }
                        }

                        // url gefunden
                        if (url != null) {
                            log.log(Level.INFO, "versuche connection: {0}", url);
                            StreamConnection connection = (StreamConnection)Connector.open(url);
                            log.log(Level.INFO, "Connection geöffnet");

                            // die verbindung testen
                            if (myRD.isAuthorized(connection) && myRD.isEncrypted()){
                                // melde erfolg
                                log.log(Level.INFO, "yippie!");
                            } else {
                                // melde grütze
                                log.log(Level.INFO, "kacka!");
                            }
                            connection.close();
                        }
                    } else {
                        log.log(Level.INFO, "keine Services gefunden");
                    }

                    // keine ahnung ob dieser zugriff zur Hid report konfiguration benötigt wird?!
                    //ServiceRecord rc = ld.getRecord(connNotifier);
                    //rc.
                    //ld.updateRecord(rc);
                }
            }
        } catch(IOException | NullPointerException ex){
            log.log(Level.INFO, "Huston we have a problem: {0}", ex.toString());
        }
    }


    public BtHidDataHandler(Logger log){
        btHandler = this;
        btHandler.log = log;
    }

    // Discovery
    @Override
    public void deviceDiscovered(RemoteDevice rd, DeviceClass dc) {
        foundRemoteDevices.add(rd);
    }
    @Override
    public void servicesDiscovered(int i, ServiceRecord[] srs) {
        foundRemoteServices.addAll(Arrays.asList(srs));
    }
    @Override
    public void serviceSearchCompleted(int i, int i1) {
        log.log(Level.INFO, "Servicesuche thread beendet");
    }
    @Override
    public void inquiryCompleted(int i) {
        log.log(Level.INFO, "Gerätesuche thread beendet");
    }

}[/CODE]


----------



## SuperPCFan (26. Mrz 2021)

Durch noch tieferes graben im Netz habe ich diese Konstante zu Tage gefördert:


```
private final UUID protocolId = BluetoothConsts.RFCOMM_PROTOCOL_UUID;
```

Wenn ich "searchServices()" damit ausführe, werden einige Services gefunden. So weit so gut.
Allerdings verwirrt mich das nur nochmehr.

Wie ich verstanden habe soll diese Funktion bestimmte Services finden, die ich benötige.
Jetzt suche ich nicht nach einem Service sondern nach einem Protokoll (ein Element aus der Lowlevel API das mich eigentlich nicht interessiert)
und ich finde einen Sack voll Services.

Muss ich jetzt manuell die url's aller Services zerlegen um an die Channel ID (die Zahl hinter dem zweiten ":" ist die Channel ID, oder?) zu kommen, 
über die ich den von mir benötigten HID Service identifizieren kann? (die MAC Adresse habe ich durch "?" ersetzt)


```
[2021-03-26 13:52:53] [INFO   ] [ID:  24] startDeviceAccessTest: Services gefunden: 
[2021-03-26 13:52:53] [INFO   ] [ID:  24] startDeviceAccessTest: url: btspp://????????????:2;authenticate=false;encrypt=false;master=false
[2021-03-26 13:52:53] [INFO   ] [ID:  24] startDeviceAccessTest: url: btspp://????????????:3;authenticate=false;encrypt=false;master=false
[2021-03-26 13:52:53] [INFO   ] [ID:  24] startDeviceAccessTest: url: btgoep://????????????:19;authenticate=false;encrypt=false;master=false
[2021-03-26 13:52:53] [INFO   ] [ID:  24] startDeviceAccessTest: url: btgoep://????????????:4;authenticate=false;encrypt=false;master=false
[2021-03-26 13:52:53] [INFO   ] [ID:  24] startDeviceAccessTest: url: btgoep://????????????:5;authenticate=false;encrypt=false;master=false
[2021-03-26 13:52:53] [INFO   ] [ID:  24] startDeviceAccessTest: url: btspp://????????????:6;authenticate=false;encrypt=false;master=false
[2021-03-26 13:52:53] [INFO   ] [ID:  24] startDeviceAccessTest: url: btspp://????????????:7;authenticate=false;encrypt=false;master=false
```


----------



## SuperPCFan (26. Mrz 2021)

Die testweise Protokol-Abfrage verschiedener Geräte hat gezeigt, dass obwohl bei vielen Geräte diverse Services "möglich" sind, diese aber nicht aktiv/geladen standardmäßig aktiv sind. Damit werden sie durch eine Serviceabfrage natürlich auch nicht gefunden.

Die Frage wie man einen Service auf dem RemoteDevice "aktivieren" kann, hat mich zwangsläufig darauf gebracht das ich bei der Client-Server-Architektur falschherum gedacht habe.

Es ist offentlich so gedacht, dass ein Gerät das einen bestimmten Service nutzen möchte diesen auch als Server anbieten muss, damit das RemoteDevice dazu "gezwungen" wird den Service ebenfalls zu aktivieren.


----------



## SuperPCFan (26. Mrz 2021)

Der Dokumentation nach habe ich eine Server Variante geschrieben. (siehe unten) Beim Verbindungsaufbau meldet ein Windows-Client zwar ein Hid-Gerät, zeitgleich aber auch einen Treiberfehler. Android meldet beim Verbindungsaufbau ein Eingabegerät, bricht die Verbindung aber dann ab.

Die Zeile:

```
log.log(Level.INFO, "Service hat Ids: ", Arrays.toString(hidService.getAttributeIDs()));
```
erzeugt

```
[2021-03-26 21:55:11] [INFO   ] [ID:  24] startDeviceAccessTest: Service hat Ids:
```
und meine Test-App bleibt in der Methode "acceptAndOpen()" stecken.

Da der Verbindungsaufbau hängen bleibt, offensichtlich keine Ids im Service gepflegt sind und die Clients Fehler melden, wäre meine Vermutung das der von "Connector.open()" erzeugt Service zwar die korrekte Id hat, aber ansonsten "leer" ist.
In den Doku's und Tutorials habe ich bisher aber nirgendwo einen Hinweis gesehen, dass ich einen Service selber füllen muss.
Werden die von der API erzeugten Services nicht mit Standardwerten gefüllt?
Das da keine einzige Attributs-Id drin sein soll verwirrt mich schonwieder. 


```
public void startDeviceAccessTest(){
        try {
            LocalDevice ld = LocalDevice.getLocalDevice();
            log.log(Level.INFO, "ich bin ein: {0}", ((Object)ld.getDeviceClass()).toString());
          
            String sUrlProtocol = "btspp"; //btspp //btl2cap //btgoep
            String sUrlAddress = "//localhost";
            UUID hidServiceId = new UUID("1124", true);
            String sUrlCtrlOptions = ";authenticate=true;authorize=true;encrypt=true";
            String url = sUrlProtocol + ":" + sUrlAddress + ":" + hidServiceId.toString() + sUrlCtrlOptions;
            log.log(Level.INFO, "biete an: {0}", url);
            StreamConnectionNotifier hidNotifier = (StreamConnectionNotifier)Connector.open(url);
          
            ServiceRecord hidService = ld.getRecord(hidNotifier);
            log.log(Level.INFO, "Service hat Ids: ", Arrays.toString(hidService.getAttributeIDs()));

            // ???

            ld.updateRecord(hidService);
            log.log(Level.INFO, "service erzeugt");
          
            ld.setDiscoverable(DiscoveryAgent.LIAC);
            log.log(Level.INFO, "Gerät begrenzt sichtbar");
          
            StreamConnection hidConnection = (StreamConnection)hidNotifier.acceptAndOpen();
            log.log(Level.INFO, "Connection geöffnet");

            // die verbindung testen
            RemoteDevice myRD = RemoteDevice.getRemoteDevice(hidConnection);
            if (myRD.isAuthenticated() && myRD.isAuthorized(hidConnection) && myRD.isEncrypted()){
                // melde erfolg
                log.log(Level.INFO, "yippie!");
            } else {
                // melde grütze
                log.log(Level.INFO, "kacka!");
            }
          
        } catch(IOException | NullPointerException ex){
            log.log(Level.INFO, "Huston we have a problem: {0}", ex.toString());
        }
    }
```


----------



## SuperPCFan (27. Mrz 2021)

Nachdem ich herausgefunden hatte, dass ich nur keine Attribut-Ids im Log sehen konnte weil ich in dem String das "{0}" vergessen hatte.........
erhalte ich zumindest schonmal diese Ids aus dem erzeugten Service:

```
hid ids: [4, 256, 1, 0]
```

Das klingt schon logischer. Das Grundkonstrukt wird automatisch angelegt.

Nur habe ich bisher keine Beschreibung gefunden wie die speziellen Ids für den Service 0x1124 gefüllt werden müssen.
Auf der Bluetoothseite stehen nur Namen und Nummern der Ids. Das hilft mir aber nicht wirklich weiter.
Es fehlt der Datentyp, ob eine Id mandatorisch ist und eine Erklärung des Inhaltes.

Diese Infos habe ich bisher auch in keinem Tutorial oder Example gesehen. Hat jemand einen Tipp?


----------



## SuperPCFan (29. Mrz 2021)

Nach weiteren drei Tagen durchwühlen des Internet hat sich herausgestellt, dass die Spezifikation für die Attribute des Human Interface Device *Service* _nicht_ in dem gleichnamigen zugehörigen Dokument zu finden sind, sondern im Dokument "Human Interface Device *Profile"*, aber nicht zu verwechseln mit "Human Interface Device *over GATT*". Alle Klarheiten beseitigt? Gut! 🙂
Falls jemand so gekonnnt wie ich um die Bluetooth-Spec-Tabelle drumherum geklickt hat, hier der Link: Bluetooth-Spec-Tabelle


----------

