Java & Bluetooth

Aprés un mois intensif de boulot je reviens au blog pour vous faire profiter de ce que j’ai appris. Le projet sur lequel je travaille en ce moment est sur de l’embarqué avec des tablettes PC. Cela m’a permis d’aborder la techno Bluetooth en Java. Après avoir parcouru le net et lu quelques articles l’utilisation de cette techno en Java est relativement simple. Le souci est simplement de trouver une implémentation qui va bien. En effet la JSR 82 reste récente et les implémentations open source ne sont pas nombreuses. J’en ai choisie 2 qui fonctionnent bien en s’appuyant sur l’API bluetooth de Windows : BlueCove et BlueSock.

Passons à la technique : le principe de fonctionnement consiste en un système client/serveur classique, basé sur un protocole particulier. Le protocole bluetooth se décompose en deux parties d’un coté les layers de l’autre les profiles ou services. Les deux schémas suivant présentent les différents layers et profiles :

Layers Profiles
Layers Profiles

Je ne rentrerais pas dans les détails ici, et vous pouvez apprendre le nécessaire dans ce bouquin. Je présenterais juste un petit exemple qui exploite le layer RFCOMM et le service port serie pour parler à mon téléphone portable et lui envoyer des commandes AT (Technical Docs & Training -> Developers’ Guidelines – AT Commands).
Première étape rechercher les devices à proximité :

LocalDevice.getLocalDevice().getDiscoveryAgent().startInquiry(DiscoveryAgent.GIAC, aDiscoveryListener);

ou aDiscoveryListener doit être une classe qui implémente l’interface DiscoveryListener. Cette méthode lance un thread de recherche et rend la main il est donc nécessaire de gérer son code avec des threads pour gérer le temps de recherche.
Chaque device trouvé fait appel à la méthode ci-dessous du DiscoveryListener :

public void deviceDiscovered(RemoteDevice btDevice, DeviceClass cod);

La fin de la recherche est notifié par la méthode suivante :

public synchronized void inquiryCompleted(int discType)

discType peut être INQUIRY_COMPLETED, INQUIRY_TERMINATED, INQUIRY_ERROR.
Ensuite pour chaque device il est nécessaire de demander quels services il fournit :

LocalDevice.getLocalDevice().getDiscoveryAgent().searchServices(new int[] {  }, new UUID[] {  }, device, aDiscoveryListener)

attrSet correspond à la liste des attributs attendus par service, uuids la liste des services que vous recherchez (vous trouverez la liste des UUID prédéfinis ici, mais vous pouvez déclarer votre propre service en lui affectant votre propre UUID). En mettant null à ces deux arguments vous recherchez par défaut tous les services et tous leurs attributs. Le paramètre device correspond au device sur lequel vous voulez faire la recherche et le paramètre aDiscoveryListener c’est l’implémentation du DiscoveryListener que vous utilisez.
Chaque service trouvé fait appel à la méthode suivante du DiscoveryListener :

public void servicesDiscovered(int transID, ServiceRecord[] servRecord)

transID est le numéro de la transaction et servRecord un tableau contenant les services trouvés.
A la fin de la recherche des services la méthode suivante est appelée :

public synchronized void serviceSearchCompleted(int transID, int respCode).

Une fois le ou les serviceRecord récupérés vous y faites appel avec la méthode suivante :

String url = aServiceRecord.getConnectionURL(ServiceRecord.NOAUTHENTICATE_NOENCRYPT, false);
StreamConnection conn = (StreamConnection) Connector.open(url);
DataOutputStream dos = conn.openDataOutputStream();
DataInputStream dis = conn.openDataInputStream();

Il ne reste plus qu’à envoyer des commandes dans le flux, par exemple ici je fais appel depuis mon PC vers mon téléphone portable pour lui demander de composer un numéro de téléphone :

dos.write(new String("ATD0141978324;\r\n").getBytes());

je lis la réponse :

int c;
while ((c = dis.read()) != -1) {
  System.out.print((char)c);
}

et je ferme les flux

dos.close();
dis.close();
conn.close();

Ce code est équivalent en J2ME et en J2SE, les devices peuvent être de tous types (PC, telephone, PDA, …) à partir du moment où ils supportent une JVM et ont un système Bluetooth pour lequel il existe une implémentation de la JSR 82.