CERCA SITEMAP FEED RSS 1280
Ultimo aggiornamento: 30 Agosto 2009

Bluetooth API

La tecnologia Bluetooth consiste di un protocollo di comunicazione a basso consumo (sfrutta 1/1000 della potenza del WIFI) ed a breve raggio (distanza massima: 100 metri).

In effetti i dispositivi Bluetooth possono essere suddivisi in tre classi:
  • classe 1: potenza di 100 mW e raggio di azione di 100 metri
  • classe 2: potenza di 2.5 mW e raggio d’azione di 10 metri
  • classe 3: potenza di 1mW e raggio d’azione di 10 cm
Essi possono instaurare fra loro delle connessioni dando vita a delle vere e proprie reti:
  • la piconet è caratterizzata da 8 dispositivi di cui un master e 7 slave: ogni comunicazione passa attraverso il master in maniera del tutto analoga ad una rete a stella.
Piconet
In ogni piconet un dispositivo Bluetooth assume il ruolo di master scegliendo la sequenza con cui cambiare la frequenza portante radio mentre gli altri assumono il ruolo di slave interagendo tra loro secondo protocolli di scambio dati.

Il master trasmette solo nei timeslot pari, mentre lo slave trasmette nei timeslot dispari al fine di evitare collisioni

E’ opportuno precisare che al master possono essere connessi anche dei dispositivi in stato di attesa oltre a quelli eventualmente attivi.
  • la scatternet è invece costituita da più piconet i cui master comunicano fra loro.
Scatternet
Una scatternet può contenere fino ad un massimo di 10 piconet per un totale di 80 dispositivi bluetooth.

I dispositivi appartenenti a piconet differenti operano a frequenze differenti onde evitare interferenze nelle comunicazioni.

Un terminale Bluetooth può appartenere contemporaneamente a due piconet purchè sia in grado di operare a frequenze differenti a seconda che comunichi con i dispositivi dell’una o dell’altra: naturalmente non può assumere il ruolo di master in entrambe le piconet contemporaneamente.
La tecnologia Bluetooth è organizzata in livelli: i più alti sono costituiti da API per la scoperta di dispositivi o di servizi, l’instaurazione di connessioni e l’implementazione di comunicazioni seriali mentre i più bassi sono costituiti da protocolli per il multiplexing, il riassemblaggio o la segmentazione.
I dispositivi che implementano la tecnologia Bluetooth devono essere necessariamente conformi ad una serie di profili che garantiscano la compatibilità fra dispositivi diversi o applicazioni scritte in linguaggi diversi.
Alcuni di questi profili sono:
  • Generic Access Profile: definisce l’uso dei livelli più bassi della struttura a stack della tecnologia Bluetooth
  • Service Discover Application Profile: definisce le specifiche relative alla scoperta di servizi e dispositivi
  • Serial Port Profile: definisce i requisiti di interoperabilità fra i livelli più bassi nelle comunicazioni seriali.
  • Generic Object Exchange Profile: definisce le caratteristiche per il trasferimento di file e/o di oggetti (OBEX) e la sincronizzazione
La comunicazione Bluetooth si basa sul paradigma Client-Server; per usufruire di un servizio un dispositivo client dovrà effettuare nell’ordine le seguenti operazioni:
  • inizializzare il dispositivo Bluetooth locale
  • ricercare dispositivi Bluetooth presenti nei paraggi
  • cercare il servizio di interesse presso i dispositivi Bluetooth scoperti
  • connettersi al servizio
  • usufruire del servizio
  • disconnettersi
L’inizializzazione consiste nell’impostare il nome simbolico del dispositivo e la visibilità nel Bluetooth Configuration Center (BCC), per ottimizzare i tempi di ricerca di dispositivi e quindi dei servizi da questi offerti è possibile accedere alla cache del BCC per recuperare i risultati dell’ultima ricerca effettuata.

I dispositivi Bluetooth trovati dopo una ricerca vengono identificati univocamente da:
  • il Bluetooth Address
  • il nome simbolico del dispositivo impostato nel BCC all’atto dell’inizializzazione (detto friendly name)
  • la classe di appartenenza del dispositivio
Quando un dispositivo remoto viene trovato e selezionato si può procedere alla ricerca dei servizi da esso forniti (potrebbe fornirne più di uno o non fornirne alcuno).
La ricerca avviene in maniera sequenziale: ad ogni servizio è associato un identificativo univoco UUID da 16 a 128 bit; trovato il servizio di interesse è possibile connettersi allo stesso utilizzando una connessione L2CAP o RFCOMM.

Il dispositivo Bluetooth che fornisce un servizio dovrà invece compiere le seguenti operazioni:
  • inizializzare il dispositivo Bluetooth locale
  • creare il servizio remoto
  • inserire il servizio remoto nel database dei servizi Bluetooth pubblicati (Service Discovery Database).
  • attendere una connessione entrante da parte di un client
  • scambiare informazioni
  • concludere la connessione
All’interno del Service Discovery Database (SDDB) per ogni servizio possono essere memorizzate informazioni descrittive dello stesso ed utilizzabili dal client per effettuare le sue ricerche.

Bluetooth e Java

In Java le API Bluetooth sono implementate dalle JSR-82 suddivise in due package: il Core Bluetooth API e l’Object Exchange API.
Le JSR-82 si adattano agli standard delle API J2ME ed al Generic Connection Framework (GCF) definito dal profilo CLDC/MIDP.

Le API del package javax.bluetooth contengono:
  • classi per la gestione dei dispositivi
  • classi per la ricerca di dispositivi e servizi
  • classi per la comunicazione

Gestione dei dispositivi

Le classi coinvolte nella gestione dei dispositivi Bluetooth sono:
  • LocalDevice
  • RemoteDevice
  • DeviceClass
La classe LocalDevice rappresenta il dispositivo Bluetooth locale, essa contiene metodi per il recupero delle informazioni identificative e dello stato del dispositivo: è possibile ad esempio recuperare il Bluetooth Address con il metodo getBluetoothAddress(), lo stato della visibilità del dispositivo getDiscovereble(), accedere al DiscoveryAgent locale getDiscoveryAgent() ed altro ancora.
La classe RemoteDevice è invece rappresentativa di un dispositivo remoto (quello che, per intenderci, potrebbe trovarsi in prossimità del dispositivo che stiamo utilizzando).
Tale classe ha molti metodi in comune con la prima soprattutto per quanto riguarda il recupero delle informazioni identificative (Bluetooth Address e friendly name) e contiene anche metodi per autorizzare, autenticare e cifrare le comunicazioni.
Il seguente codice ad esempio controlla se la connessione utilizzata dal RemoteDevice è criptata e se non lo è prova a criptarla.
...

try
{
   con = (StreamConnection) Connector.open(url);
   remoteDevice = RemoteDevice.getRemoteDevice(con);
   remoteAddress = remoteDevice.getBluetoothAddress();
   remoteName = remoteDevice.getFriendlyName();
   if (!remoteDevice.isEncrypted())
   {
      if (!remoteDevice.authenticate() || !remoteDevice.encrypt(con, true))
      {
         ...
      }
   }
}
catch (Exception e)
{
   ...
}

...
La DeviceClass, recuperabile dal LocalDevice con il metodo getDeviceClass(), definisce infine la classe del dispositivo (se è un telefonino, un portatile o altro ancora).
La DeviceClass potrebbe essere utilizzata per dar vita a differenti tipi di elaborazione a seconda del dispositivo locale che si sta utilizzando come nell’esempio seguente:
...

static final PHONE_MAJOR_CLASS = 0x200;
static final CELLULAR_MINOR_CLASS = 0x04;

...

try
{
   LocalDevice localDevice = LocalDevice.getLocalDevice();
   DeviceClass deviceClass = localDevice.getDeviceClass();
   if (deviceClass.getMajorDeviceClass() == PHONE_MAJOR_CLASS)
   {
      if (deviceClass.getMinorDeviceClass() == CELLULAR_MINOR_CLASS)
      {
         ...
      }
   }
}
catch (Exception e)
{
   ...
}

...

La ricerca di dispositivi e servizi

La ricerca di dispositivi e servizi avviene mediante l’utilizzo di un oggetto DiscoveryAgent accessibile dal LocalDevice attraverso il metodo getDiscoveryAgent().
Tale oggetto contiene metodi per la ricerca di dispositivi Bluetooth nelle vicinanze, ricerca di servizi, recupero di dispositivi dalla cache del BCC, selezione di un servizio ed infine cancellazione dei risultati delle ultime ricerche effettuate.
La ricerca di dispositivi avviene utilizzando il metodo startInquiry(int accessCode, DiscoveryListener listener) della classe DiscoveryAgent: occorre specificare come parametri un access code GIAC (General Inquiry Access Code - significa che tutti i dispositivi remoti sono in grado di visualizzare il dispositivo) o LIAC (Limited Inquiry Access Code - il dispositivo ha visibilità limitata) ed una classe che implementi l’interfaccia DiscoveryListener.
L’interfaccia DiscoveryListener contiene fra gli altri due metodi: deviceDiscovered(RemoteDevice btDevice, DeviceClass cod) che viene invocato quando viene trovato un dispositivo remoto durante una ricerca ed inquiryCompleted(int discType) che viene invocato quando un’operazione di ricerca di dispositivi remoti termina.
La ricerca di servizi avviene utilizzando il metodo searchServices(int[] attrSet, UUID[] uuidSet, RemoteDevice btDev, DiscoveryListener discListener) dell’oggetto DiscoveryAgent: tale metodo vuole come parametri un set di UUID ovvero di identificatori univoci di servizi di interesse, il RemoteDevice sul quale effettuare la ricerca di servizi (e che potrebbe essere stato recuperato in seguito ad una ricerca di dispositivi remoti) ed infine una classe che implementi l’interfaccia DiscoveryListener.
Oltre ai metodi descritti in precedenza, l’interfaccia DiscoveryListener contiene due metodi che entrano in gioco quando viene effettuata una ricerca di servizi offerti da un dispositivo remoto: servicesDiscovered(int transID, ServiceRecord[] servRecord) che viene invocato quando viene trovato un servizio che risponde alle caratteristiche precisate e servicesSearchCompleted(int transID, int respCode) che viene invocato quando termina la ricerca di servizi.

Esempio di MIDlet che ricerca dispositivi Bluetooth e servizi:
...

public class SearchMIDlet implements DiscoveryListener
{
   LocalDevice localDevice = LocalDevice.getLocalDevice();
   DiscoveryAgent discoveryAgent = localDevice.getDiscoveryAgent();
   Vector deviceList = new Vector();
   ServiceRecord serviceRecord;

   ...

   private void discover()
   {
      serviceAgent.startInquiry(DiscoveryAgent.GIAC, this);
      devList = discoveryAgent.retrieveDevices(DiscoveryAgent.CACHED);
      serviceRecord = searchServices(devList);
   }

   private boolean searchServices(RemoteDevice[] devList)
   {
      UUID[] searchList = new UUID[2];
      searchList[0] = new UUID(0x0100);
      searchList[1] = new UUID(MY_SERVICE_UUID, false);
      for (int i = 0; i != devList.length; i++)
      {
         try
         {
            int trans;
            trans = discoveryAgent.searchServices(null, searchList, devList[i], this);
         }
         catch (BluetoothStateException e)
         {
            ...
         }
      }
   }

   public void servicesDiscovered(int transID, ServiceRecord[] servRecord)
   {
      if (serviceRecord != null)
      serviceRecord = servRecord[0];
   }

   public void deviceDiscovered(RemoteDevice btDevice, DeviceClass cod)
   {
      deviceList.addElement(btDevice);
   }

   public void serviceSearchCompleted(int transID, int respCode)
   {
      ...
   }

   public void inquiryCompleted(int discType)
   {
      ...
   }
}

...
Ogni servizio ed attributo del servizio è identificato univocamente da un UUID: i servizi offerti sono memorizzati nel Service Discovery Database (SDDB) sotto forma di ServiceRecord.
La richiesta di ServiceRecord avviene tramite l’utilizzo di un protocollo denominato Service Discovery Protocol (SDP).
Per rendere disponibile un servizio ai clienti, un’applicazione server crea un ServiceRecord instaurando una connessione di tipo ConnectionNotifier e successivamente inserisce il ServiceRecord nell’SDDB invocando il metodo acceptAndOpen() della classe ConnectionNotifier.
Un ServiceRecord rappresenta quindi un servizio all’interno del SDDB, la classe ServiceRecord contiene pertanto metodi per il recupero e la definizione degli attributi del servizio stesso (getAttributeValue(int attrID) e setAttributeValue(int attrID, DataElement attrValue)): ogni attributo è rappresentato da un oggetto DataElement.
Service Discovery Database
La tipica organizzazione del Service Discovery Database prevede un ServiceRecord per servizio.

Gli attributi del servizio vengono memorizzati mediante collezioni di oggetti DataElement come mostrato nella figura a lato.

La comunicazione

Per creare connessioni client o server si utilizza il metodo open(String url, int mode, boolean timeout) della classe Connector specificando nella versione più completa url (che deve soddisfare un determinato scheme), modo della connessione (lettura, scrittura, lettura/scrittura) e timeout cioè un valore booleano che indica che il "chiamante" vuole ricevere le notifiche sulle eccezioni da timeout o meno.

Per creare una connessione RFCOMM di tipo client si può utilizzare la seguente forma:
StreamConnection con = (StreamConnection) Connector.open("btspp://host:port")
Per creare connessioni di tipo L2CAP:
L2CAPConnection con = (L2CAPConnection) Connector.open("btl2cap://host:port");
L’indirizzo di un servizio trovato a seguito di una ricerca può essere recuperato tramite il metodo getConnectionURL() della classe ServiceRecord.

Per creare una connessione RFCOMM di tipo server si può utilizzare la seguente forma:
StreamConnectionNotifier cn = (StreamConnectionNotifier) Connector.open("btspp:// Localhost: UUID;name=nomeservizio");
dove l’UUID è l’identificativo univoco del servizio e name contiene il valore del friendly name.

Analogamente per creare una connessione L2CAP lato server si può scrivere:
L2CAPConnectionNotifier = cn (L2CAPConnectionNotifier) Connector.open("btl2cap://localhost: UUID;name=nomeservizio");
Prima di creare una connessione è indispensabile rendere visibile il dispositivo Bluetooth tramite il metodo setDiscoverable(int mode) della classe LocalDevice
LocalDevice local = LocalDevice.getLocalDevice(); local.setDiscoverable(DiscoveryAgent.GIAC);
Una volta creata una connessione il Server resta in attesa di una richiesta client tramite il metodo acceptAndOpen() della classe ConnectionNotifier.
Tale metodo provvede a registrare automaticamente il servizio nel SDDB locale, è possibile aggiornare gli attributi del servizio o accedere agli stessi utilizzando il metodo getRecord(javax.microedition.io.Connection notifier) della classe LocalDevice indicando come parametro la connessione utilizzata.