Nel precedente articolo abbiamo discusso i concetti principali del Bluetooth e della specifica JSR-82, realizzando il discovery dei dispositivi Bluetooth a portata di comunicazione. In questo articolo concluderemo l'argomento parlando del discovery dei servizi e del protocollo OBEX per il trasferimento dei file. Tali concetti serviranno a completare il nostro esempio.
Il Service Discovery
La fase successiva al discovery dei dispositivi a portata di comunicazione è la ricerca dei servizi da essi offerti (service discovery): i servizi infatti, sono le "porte" attraverso le quali avviene lo scambio dei dati. Esistono diverse tipologie di servizi, ciascuna basata su protocolli di comunicazione specifici. Noi ci concentreremo in particolare modo sul servizio di scambio file basato su protocollo OBEX. Un altro tipo di servizio, ad esempio, è lo scambio di stream di dati, attraverso il già citato protocollo RFCOMM. Prima di passare a discutere il service discovery nella specifica JSR-82, dobbiamo però introdurre un concetto fondamentale: l’indirizzamento di un servizio. In generale infatti, per poter richiamare un qualunque tipo di servizio, deve necessariamente esistere un modo per poterlo indirizzare univocamente. Nello standard Bluetooth, le parole chiave per la soluzione di questo problema sono: UUID e URL. L’UUID è un identificativo univoco a 16, 32 o 128 bit, che viene utilizzato in fase di service discovery per indirizzare il servizio desiderato. Per i servizi standard (offerti dalla maggior parte dei dispositivi commerciali, quali ad esempio i telefoni cellulari), come nel caso dello scambio di file mediante protocollo OBEX, l’UUID è noto a priori (per intenderci è un po’ quello che accade con le porte TCP fino alla 1024). Ogni volta che, durante il service discovery, verrà individuato un servizio con l’UUID specificato, all’applicazione verrà restituito un URL attraverso cui il servizio potrà essere richiamato. Tale URL è così formato:
scheme://host:port;paramters
dove:
- lo scheme rappresenta il protocollo di comunicazione utilizzato dal servizio
- host rappresenta l'indirizzo Bluetooth del dispositivo che ospita il servizio
- la port è utilizzata come multiplexer del servizio
- i parameters rappresentano dei parametri opzionali, specifici del tipo di servizio
Le classi della specifica JSR-82 per il Service Discovery
Come visto nel precedente articolo, l'interfaccia javax.bluetooth.DiscoveryListener consente all'applicazione di ricevere gli eventi legati al device discovery e al service discovery. L'applicazione utente deve quindi fornire un'implementazione ai metodi di quest'interfaccia per elaborare tali eventi. Per quel che riguarda la fase di service discovery, ogni volta che su un dispositivo viene trovato un insieme di nuovi servizi, la libreria Java Bluetooth richiamerà il metodo servicesDiscovered, fornendo come argomento un array di tipo javax.bluetooth.ServiceRecord che rappresenta i servizi individuati (in particolare contiene gli URL che ci servono per richiamarli, ottenibili attraverso il metodo getConnectionURL). Al termine della procedura sarà invocato il serviceSearchCompleted, avente come argomento un codice che indica se l’operazione è stata completata correttamente o se si è verificato un errore. La procedura di discovery dei servizi ha inizio (in un thread separato) quando viene invocato il metodo searchServices della classe javax.bluetooth.DiscoveryAgent, a cui viene dato in ingresso il dispositivo remoto (RemoteDevice) su cui effettuare la ricerca, l’array degli UUID dei servizi da cercare e l’implementazione dell’interfaccia DiscoveryListener per la ricezione e l’elaborazione degli eventi di discovery. Per trasformare in pratica questi concetti, riprendiamo il codice del precedente articolo e modifichiamo le opportune funzioni al fine di effettuare il service discovery:
import java.io.*;
import java.util.*;
import javax.bluetooth.*;
public class DiscoveryListenerImpl implements DiscoveryListener {
public void inquiryCompleted(int param) {
UUID[] uuidSet = {new UUID(0x1105)};
int[] attrSet = {0x100};
Enumeration<RemoteDevice> enumeration = devices.elements();
while(enumeration.hasMoreElements()) {
RemoteDevice device = enumeration.nextElement();
try {
if (device.getFriendlyName(true).equals("MioCell")) {
DiscoveryAgent discoveryAgent = this.discoveryAgent.getDiscoveryAgent();
discoveryAgent.searchServices(attrSet, uuidSet, device, this);
}
} catch (IOException e) {
System.err.println("Impossibile recuperare il nome del dispositivo: "
+ device.getBluetoothAddress());
}
}
} else {
System.out.println("Nessun dispositivo trovato.");
}
}
import java.io.; import java.util.; import javax.bluetooth.*;
public class DiscoveryListenerImpl implements DiscoveryListener { private ServiceRecord currentServiceRecord;
public void servicesDiscovered(int arg0, ServiceRecord[] serviceRecord) { this.currentServiceRecord = serviceRecord[0]; }
public void serviceSearchCompleted(int arg0, int responseCode) { if (responseCode == DiscoveryListener.SERVICESEARCHCOMPLETED) { / La funzione spedisce un file tramite OBEX all'URL specificato / pushImage(this.currentServiceRecord.getConnectionURL(0, false)); } else { System.out.println("Nessun servizio trovato."); } // ... } }
private void pushImage(String serviceURL) { try { String contentType = null; File file = new File("C:/tmp/prova_img.gif");
/ La seguente parte di codice forza lo schema ad essere btgoep, poiché su alcuni dispositivi l'URL viene restituito con lo schema sbagliato / String token = "://"; int tokenIndex = serviceURL.indexOf(token); String adr = "btgoep://" + serviceURL.substring(tokenIndex + token.length());
/ Inizio connessione OBEX / ClientSession cs; cs = (ClientSession) OBEXConnector.open(adr);
HeaderSet hs; hs = cs.connect(cs.createHeaderSet()); hs.setHeader(HeaderSet.NAME, fileName);
// Il content type dipende dal formato del file da spedire: // image/jpeg, audio/mp3, etc. hs.setHeader(HeaderSet.TYPE, "image/gif"); hs.setHeader(HeaderSet.LENGTH, new Long(file.length()));
Operation po = cs.put(hs); OutputStream os = po.openOutputStream();
/ Lettura del file / FileInputStream fin = new FileInputStream(file); byte[] inBuffer = new byte[1024]; int bytesRead = -1;
/ Scrittura dell'input stream del file sull'output stream della connessione / while ((bytesRead = fin.read(inBuffer)) != -1) { os.write(inBuffer, 0, bytesRead); }
os.flush(); os.close(); po.close(); cs.disconnect(null); cs.close(); fin.close(); } catch (Exception e) { System.out.println("Si è verificato il seguente errore: " + e.getMessage()); } }
