/Users/detro/workspace-nb/JAEBI-BlueToothJ2MEClient/src/org/jaebi/midlet/bt/Discoverer.java

Vai alla documentazione di questo file.
00001 /*
00002  * Discoverer.java
00003  *
00004  * Created on 30 maggio 2005, 15.58
00005  *
00006  * To change this template, choose Tools | Options and locate the template under
00007  * the Source Creation and Management node. Right-click the template and choose
00008  * Open. You can then make changes to the template in the Source Editor.
00009  *TODO deve essere un singleton..o no? cioè se lo faccio come singleton, poi posso cmq fare in modo che due applicazioni diverse in esecuzione nello stesso momento usino 2 discoverer differenti?
00010  */
00011 
00012 package org.jaebi.midlet.bt;
00013 import java.util.Enumeration;
00014 import java.util.Hashtable;
00015 import javax.bluetooth.BluetoothStateException;
00016 import javax.bluetooth.DeviceClass;
00017 import javax.bluetooth.DiscoveryAgent;
00018 import javax.bluetooth.DiscoveryListener;
00019 import javax.bluetooth.LocalDevice;
00020 import javax.bluetooth.RemoteDevice;
00021 import javax.bluetooth.ServiceRecord;
00022 import javax.bluetooth.UUID;
00023 import org.jaebi.midlet.bt.btException.BtInitException;
00024 import org.jaebi.midlet.bt.btException.DiscoveryException.DiscoveryInterruptionException;
00025 import org.jaebi.midlet.bt.btException.DiscoveryException.InquiryErrorException;
00026 import org.jaebi.midlet.bt.btException.DiscoveryException.StartInquiryException;
00027 import org.jaebi.midlet.bt.btException.DiscoveryException.UnexpectedDiscoveryCodeException;
00028 import org.jaebi.midlet.bt.btException.DiscoveryException.UnexpectedInterruptionException;
00029 import org.jaebi.midlet.bt.btException.DiscoveryException.UserInterruptionException;
00030 import org.jaebi.midlet.util.Observable;
00031 import org.jaebi.midlet.util.Observer;
00032 import org.jaebi.midlet.bt.discoveryHandling.DiscoveryReport;
00033 import org.jaebi.midlet.bt.discoveryHandling.ServiceSearchError;
00034 import org.jaebi.midlet.bt.discoveryHandling.DeviceSearchError;
00035 import org.jaebi.midlet.bt.discoveryHandling.DiscoveryError;
00036 
00078 public class Discoverer extends Observable implements DiscoveryListener, DiscovererState, Runnable {
00079     
00080     // riferimento alla local Device relativa alla periferica bluetooth su cui è in esecuzione il discoverer
00081     //TODO commentare meglio (cos'è una LocalDevice?vedi javadoc)
00082     //TODO forse non è necessario che sia una variabile di istanza()probabilmente verrà usata solo nel costruttore per prendere il discovery
00083     LocalDevice localDevice;
00084     
00085     //riferimento al discovery agent relativo alla device bluetooth su cui è in esecuizione il discoverer
00086     //TODO commentare meglio (cos'è una discoveryAgent? vedi javadoc)
00087     private DiscoveryAgent discoveryAgent;
00088     
00090     private int state = READY;
00091     
00095     private DiscoveryReport discoveryReport;
00096     
00109     private Hashtable serviceSearches;
00110     
00112     private int discType;
00113     
00114     private boolean closed;
00115     
00119     private UUID UUIDset[];
00120     
00124     private int attrset[];
00127     /* TODO forse è necessario implementarlo come thread perchè la wait in searchDevices() potrebbe
00128      * fare andare tutto in deadlock (se il thread principale si sospende può essere che la chiamata del sistema a inquiryCompleted()
00129      * non ha effetto e quindi non avviene mai la chiamata a notify
00130      */
00131     public Discoverer(DiscovererObserver discovererObserver, UUID UUIDset[], int attrset[] ) throws BtInitException{
00132         closed = false;
00133         this.discoveryReport = new DiscoveryReport();
00134         
00135         try{
00136             // crea/ottiene un localDevice e un discovery agent
00137             localDevice = LocalDevice.getLocalDevice();
00138             discoveryAgent = localDevice.getDiscoveryAgent();
00139         }catch (BluetoothStateException e1) {
00140             throw new BtInitException("Impossibile inizializzare la periferica bluetooth");
00141         }
00142         this.addObserver((Observer)discovererObserver);
00143         this.UUIDset = UUIDset;
00144         this.attrset = attrset;
00145     }
00146     
00147     public Discoverer(DiscovererObserver discovererObservers[], UUID UUIDset[], int attrset[])throws BtInitException{
00148         closed = false;
00149         this.discoveryReport = new DiscoveryReport();
00150         
00151         try{
00152             // crea/ottiene un localDevice e un discovery agent
00153             localDevice = LocalDevice.getLocalDevice();
00154             discoveryAgent = localDevice.getDiscoveryAgent();
00155         }catch (BluetoothStateException e1) {
00156             throw new BtInitException("Impossibile inizializzare la periferica bluetooth");
00157         }
00158         
00159         for (int i =0; i<discovererObservers.length; i++){
00160             this.addObserver((Observer)discovererObservers[i]);
00161         }
00162         this.UUIDset = UUIDset;
00163         this.attrset = attrset;
00164     }
00165     
00166     
00167     public boolean isClosed(){
00168         return this.closed;
00169     }
00170     
00171     
00172     public int getState(){
00173         return this.state;
00174     }
00175     
00176     
00177     private void setState(int state){
00178         this.state = state;
00179         setChanged();
00180     }
00181     
00182     
00189     private synchronized void searchDevices() throws StartInquiryException, DiscoveryInterruptionException, InquiryErrorException, UnexpectedDiscoveryCodeException{
00190         
00191         try {
00192             discoveryAgent.startInquiry(DiscoveryAgent.GIAC, this);
00193         } catch (BluetoothStateException e) {
00194             setState(DISCOVERY_ERROR);
00195             throw new StartInquiryException("Impossibile iniziare l'inquiry; impossibile iniziare la ricerca di device");
00196         }
00197         
00198         try {
00199             // sospendo il thread finchè non viene terminata la ricerca delle device (cioè finchè il sistema non effettua la chiamata a inquiryCompleted()
00200             wait();
00201         } catch (InterruptedException e) {
00202             setState(DISCOVERY_ERROR);
00203             throw new UnexpectedInterruptionException("Ricerca device terminata a causa di un'interruzione inaspettata");
00204         }
00205         
00206         // il thread è stato risvegliato per una chiamata a destroy()?
00207         if (isClosed()) {
00208             setState(DISCOVERY_ERROR);
00209             throw new DiscoveryInterruptionException("Discovery interrotto");
00210         }
00211         
00212         /* no? Allora a questo punto il sistema ha terminato l'inquiry, avrà invocato
00213          * inquiryCompleted() che a sua volta avrà ricopiato il codice di
00214          * ritorno nella var di istanza discType
00215          */
00216         switch (discType) {
00217             case INQUIRY_ERROR:
00218                 setState(DISCOVERY_ERROR);
00219                 throw new InquiryErrorException("errore durante la ricerca di device");
00220             case INQUIRY_TERMINATED:
00221                 
00222                 /*
00223                  * Se si è arrivati qui, significa che è stata fatta una chiamata
00224                  * a cancelSearch()
00225                  */
00226                 setState(DISCOVERY_ERROR);
00227                 throw new UserInterruptionException("Ricerca delle device interrotta");
00228                 
00229             case INQUIRY_COMPLETED:
00230                 
00231                 break;
00232             default:
00233                 setState(DISCOVERY_ERROR);
00234                 //discType sconosciuto
00235                 destroy();
00236                 throw new UnexpectedDiscoveryCodeException("Errore di sistema: discovery code sconosciuto");
00237         }
00238     }
00239     
00240     
00252     private synchronized void searchServices() throws DiscoveryInterruptionException{
00253         serviceSearches = new Hashtable();
00254         Enumeration devices = discoveryReport.getDevices();
00255         DiscoveryError discoveryError = null;
00256         
00257         //TODO debug
00258         System.out.println("sono il client e sto cercando questi UUID");
00259         for (int i=0; i<UUIDset.length; i++)
00260             System.out.println(UUIDset[i]);
00261         System.out.println("sono il client e sto cercando questi attr");
00262         for (int i=0; i<attrset.length; i++)
00263             System.out.println(attrset[i]);
00264         //fine debug
00265         
00271         while (devices.hasMoreElements()) {
00272             RemoteDevice rd = (RemoteDevice) devices.nextElement();
00273             //TODO debug
00274             System.out.println("sono il client ed ho indirizzo " + localDevice.getBluetoothAddress() );
00275             System.out.println("sono il client ed ho trovato un server con indirizzo " + rd.getBluetoothAddress() );
00276             
00277             try {
00278                 int id = discoveryAgent.searchServices(null, UUIDset,rd, this);
00279                 serviceSearches.put(new Integer(id), rd );
00280             } catch (BluetoothStateException e) {
00287                 String errorMsg = "Impossibile ricercare i servizi offerti dalla device ";
00288                 
00289                 discoveryError = new ServiceSearchError(ServiceSearchError.CANT_START_SEARCH, errorMsg, rd);
00290                 setState(NEW_SERVICE_SEARCH_ERROR);
00291                 notifyObservers(discoveryError);
00292                 setState(SERVICE_SEARCH);
00293                 notifyObservers();
00294             }
00295             
00296             try {
00297                 /*
00298                  * sospendo il thread finchè tutte le ricerche di servizi non
00299                  * siano terminate
00300                  */
00301                 wait();
00302             } catch (InterruptedException e) {
00303                 setState(DISCOVERY_ERROR);
00304                 throw new UnexpectedInterruptionException("Ricerca servizi terminata a causa di un'interruzione inaspettata");
00305             }
00306             
00307             // il thread è stato risvegliato per una chiamata a destroy()?
00308             if (isClosed()) {
00309                 setState(DISCOVERY_ERROR);
00310                 throw new DiscoveryInterruptionException("Discovery interrotto");
00311             }
00312             
00313              /* no? Allora a questo punto il sistema ha terminato la ricerca,
00314               * avrà invocato ServiceSearchCompleted() -in particolare avrà
00315               * fatto tante chiamate a questo metodo quante sono le ricerche di
00316               * servizi richieste- che a sua volta avrà opportunamente "riempito"
00317               * respCode
00318               */
00319         }
00320     }
00321     
00322     
00323     public void run(){
00324         discoveryReport.clear();
00325         DiscoveryError discoveryError = null;
00326         
00327         /*_____________________________________________________________________
00328          * comincio la scansione delle device
00329          * in caso di errore, istanzio opportunamente deviceError e chiamo
00330          * setChanged per la notifica agli observer
00331          *_____________________________________________________________________
00332          */
00333         setState(DEVICE_SEARCH);
00334         notifyObservers();
00335         //TODO nei catch di qui sotto deve andare un return (che continuo a fare se la ricerca delle device ha dato errore?
00336         //TODO nei casi di errori gravi, svuotare discoveryreport
00337         try{
00338             searchDevices();
00339         }
00340         
00341         catch(StartInquiryException e){
00342             discoveryError = new DeviceSearchError(DeviceSearchError.CANT_START_INQUIRY, e.getMessage());
00343         }
00344         
00345         catch (DiscoveryInterruptionException e1){
00346             
00347             /***********************************************************
00348              * l'inquiry è stato interrotto; sono 3 le possibili cause:
00349              ***********************************************************/
00350             
00351             String errorMsg = e1.getMessage();
00352             Exception ex;
00353             
00354             try{
00355                 
00356                 /*************************************************
00357                  * 1) è stata fatta una chiamata a cancelSearch()
00358                  *************************************************/
00359                 
00360                 ex =(UserInterruptionException)e1;
00361                 discoveryError = new DeviceSearchError(DeviceSearchError.INQUIRY_TERMINATED, errorMsg);
00362             } catch (ClassCastException ex1){
00363                 
00364                 try{
00365                     
00366                     /**************************************************
00367                      * 2) la wait ha lanciato un InterruptedException
00368                      **************************************************/
00369                     
00370                     ex = (UnexpectedInterruptionException)e1;
00371                     discoveryError = new DiscoveryError(DeviceSearchError.UNEXPECTED_DISCOVERY_INTERRUPTION, errorMsg);
00372                 } catch (ClassCastException ex2){
00373                     
00374                     /******************************************
00375                      * 3) è stata fatta una chiamata a destroy()
00376                      ******************************************/
00377                     
00378                     discoveryError = new DiscoveryError(DeviceSearchError.DISCOVERY_INTERRUPTION, errorMsg);
00379                 }
00380                 
00381             }
00382             
00383         }
00384         
00385         catch (InquiryErrorException e2){
00386             discoveryError = new DeviceSearchError(DeviceSearchError.INQUIRY_ERROR, e2.getMessage());
00387         }
00388         
00389         catch(UnexpectedDiscoveryCodeException e3){
00390             discoveryError = new DiscoveryError(DeviceSearchError.UNKNOWN_DISCOVERY_CODE, e3.getMessage());
00391         }
00392         
00393         notifyObservers(discoveryError);
00394         
00398         if( (discoveryError != null) || (discoveryReport.deviceCount()==0) ){
00399             setState(READY);
00400             notifyObservers();
00401             return;
00402         }
00403         
00404         /*______________________________________________________________________
00405          * Comincio la scansione dei servizi
00406          *______________________________________________________________________
00407          */
00408         setState(SERVICE_SEARCH);
00409         
00410         /*
00411          * notifico agli observer che lo stato del discoverer è cambiato ed
00412          * invio loro la lista delle devices trovate
00413          */
00414         notifyObservers();
00415         
00416         try{
00417             searchServices();
00418         }
00419         
00420         catch (DiscoveryInterruptionException e) {
00421             Exception ex;
00422             String errorMsg = e.getMessage();
00423             try{
00424                 ex = (UnexpectedInterruptionException)e;
00425                 discoveryError = new DiscoveryError(DiscoveryError.UNEXPECTED_DISCOVERY_INTERRUPTION, errorMsg);
00426             } catch (ClassCastException e1){
00427                 discoveryError = new DiscoveryError(DiscoveryError.DISCOVERY_INTERRUPTION, errorMsg);
00428             }
00429         }
00430         
00431         notifyObservers(discoveryError);
00432         
00433         setState(READY);
00434         notifyObservers();
00435         
00436     }
00437     
00438     
00442     public void scanDevicesServices(){
00443         closed = false;
00444         new Thread(this).start();
00445     }
00446     
00447     
00452     public void deviceDiscovered(RemoteDevice btDevice,
00453             DeviceClass cod){
00454         
00455         discoveryReport.addDevice(btDevice);
00456         
00457         //TODO debug
00458         System.out.println("device trovata: " + btDevice.getBluetoothAddress());
00459     }
00460     
00461     
00465     public void inquiryCompleted(int discType){
00466         
00467         /*
00468          * Ricopio il discType ritornato dal sistema nell'opportuna var di
00469          * istanza
00470          */
00471         this.discType = discType;
00472         
00473         /* risveglio il thread (lanciato da scanDevicesServices()) che era in
00474          * attesa del completamento dell'inquiry
00475          */
00476         //TODO capire perchè è una reg critica
00477         synchronized (this) {
00478             notify();
00479         }
00480     }
00481     
00482     
00487     public void servicesDiscovered(int transID,ServiceRecord[] servRecord){
00488         //TODO debug
00489         System.out.println("servizio scoperto");
00490         
00491         for (int i = 0; i < servRecord.length; i++) {
00492             discoveryReport.addService(servRecord[i]);
00493             
00494         }
00495     }
00496     
00497     
00498     //TODO deve essere synchronized?
00500     public void cancelSearch() {
00501         synchronized (this) {
00502             if (state == DEVICE_SEARCH) {
00503                 discoveryAgent.cancelInquiry(this);
00504             } else if (state == SERVICE_SEARCH) {
00505                 Enumeration en = serviceSearches.keys();
00506                 while (en.hasMoreElements()){
00507                     int id = ((Integer)en.nextElement()).intValue();
00508                     discoveryAgent.cancelServiceSearch(id);
00509                 }
00510             }
00511         }
00512     }
00513     
00514     
00515     //TODO deve essere synchronized?
00516     public void cancelServiceSearch(int id){
00517         discoveryAgent.cancelServiceSearch(id);
00518     }
00519     
00520     
00521     //TODO deve essere synchronized?
00522     public void cancelAllPendigServiceSearch(){
00523         Enumeration en = serviceSearches.keys();
00524         
00525         while (en.hasMoreElements()){
00526             int id = ((Integer)en.nextElement()).intValue();
00527             discoveryAgent.cancelServiceSearch(id);
00528         }
00529     }
00530     
00536     public void serviceSearchCompleted(int transID, int respCode){
00537         boolean error = false;
00538         String errorMsg;
00539         
00540         /*
00541          * Recupero la device su cui è stata sottomessa la ricerca con
00542          * id = transid
00543          */
00544         Integer id = new Integer(transID);
00545         RemoteDevice rd = (RemoteDevice) serviceSearches.get(id);
00546         
00547         //TODO debug
00548         if (rd != null){
00549             System.out.println("è finita la ricerca di servizi per la device con indirizzo " + rd.getBluetoothAddress());
00550             System.out.println("ci sono " + discoveryReport.getServices(rd).size()+" servizi associati a questa device");
00551         } else
00552             System.out.println("trans id inatteso");
00553         
00554         DiscoveryError discoveryError = null;
00555         
00556         
00557         if (!serviceSearches.containsKey(id)) {
00558             /*******************************************************************
00559              * error - l'id della ricerca appena terminata non era tra gli id
00560              * delle nostre richieste
00561              ******************************************************************/
00562             
00563             error = true;
00564             errorMsg = "Id ricerca non previsto";
00565             setState(NEW_SERVICE_SEARCH_ERROR);
00566             discoveryError = new ServiceSearchError(ServiceSearchError.UNEXPECTED_TRANS_ID, errorMsg, rd);
00567             
00568             // TODO devo far fare qualcos'altro?
00569         }
00570         
00571         //aggiorno la lista delle ricerche in pending
00572         this.serviceSearches.remove(id);
00573         
00574         
00575         /*
00576          * se l'id della ricerca terminata non è tra quelle pending, è inutile
00577          * che controllo il suo respCode
00578          */
00579         if (!error){
00580             switch (respCode){
00581                 
00582                 case SERVICE_SEARCH_ERROR:
00583                     setState(NEW_SERVICE_SEARCH_ERROR);
00584                     errorMsg = "Errore durante la ricerca di servizi";
00585                     discoveryError = new ServiceSearchError(ServiceSearchError.SEARCH_ERROR, errorMsg, rd);
00586                     break;
00587                     
00588                 case SERVICE_SEARCH_TERMINATED:
00589                 /*
00590                  * Se si è arrivati qui, significa che è stata fatta una chiamata
00591                  * a cancelServiceSearch() o cancelAllPendingSrviceSearch
00592                  */
00593                     setState(NEW_SERVICE_SEARCH_ERROR);
00594                     errorMsg = "Ricerca dei servizi interrotta";
00595                     discoveryError = new ServiceSearchError(ServiceSearchError.SEARCH_TERMINATED, errorMsg, rd);
00596                     break;
00597                     
00598                 case SERVICE_SEARCH_COMPLETED:
00599                     setState(this.NEW_SERVICE_SEARCH_COMPLETE);
00600                     
00601                     /* chiamo un notifyObservers perchè voglio che sia inviato
00602                      * loro la device su cui è terminata la ricerca così poi attravesro
00603                      *observable recupero i servizxi
00604                      */
00605                     //TODO perchè mi da class cast exception???
00606                     notifyObservers(rd);
00607                     break;
00608                     
00609                 case SERVICE_SEARCH_NO_RECORDS:
00610                     setState(NEW_SERVICE_SEARCH_ERROR);
00611                     errorMsg = "Per questa device non è stato trovato alcun record di servizio compatibile con quelli richiesti";
00612                     discoveryError = new ServiceSearchError(ServiceSearchError.SEARCH_TERMINATED, errorMsg, rd);
00613                     break;
00614                     
00615                 case SERVICE_SEARCH_DEVICE_NOT_REACHABLE:
00616                     setState(NEW_SERVICE_SEARCH_ERROR);
00617                     errorMsg = "Per questa device non è possibile continuare la ricerca di servizi perchè la device risulta non raggiungibile";
00618                     discoveryError = new ServiceSearchError(ServiceSearchError.SEARCH_TERMINATED, errorMsg, rd);
00619                     break;
00620                     
00621                 default:
00622                     setState(NEW_SERVICE_SEARCH_ERROR);
00623                     errorMsg = "Response code sconosciuto";
00624                     discoveryError = new ServiceSearchError(ServiceSearchError.UNKNOWN_DISCOVERY_CODE, errorMsg, rd);
00625                     break;
00626             }
00627         }
00628         
00629         
00637         notifyObservers(discoveryError);
00638         
00639         /*
00640          ***************************************************************
00641          * controllo che la ricerca terminata sia l'ultima sottomessa
00642          ****************************************************************
00643          */
00644         
00645         /*
00646          * Se c'è ancora almeno una ricerca che deve terminare, reimposto lo
00647          * stato a SERVICE_SEARCH ed effettuo un return. Le operazioni
00648          * successive a questo if verranno effettuate solo
00649          * nel momento in cui termina l'ultima ricerca di servizi sottomessa
00650          */
00651         if (serviceSearches.size() > 0) {
00652             setState(this.SERVICE_SEARCH);
00653             notifyObservers();
00654             return;
00655         }
00656         
00657         /*
00658          * ok, tutte le ricerche di servizi richieste sono state completate
00659          * risveglio il thread sospeso che, dopo aver effettuato i controlli
00660          * sull'eventuale occorrenza di errori, imposterà
00661          * lo stato dell discoverer a READY
00662          */
00663         synchronized (this) {
00664             //risveglio il thread sospeso
00665             notify();
00666         }
00667     }
00668     
00669     
00670     public DiscoveryReport getDiscoveryReport(){
00671         return discoveryReport;
00672     }
00673     
00674     
00678     public void destroy(){
00679         /* risveglio l'eventuale thread in attesa che la scansione delle device
00680          * o dei servizi termini
00681          */
00682         notify();
00683         cancelSearch();
00684         closed = true;
00685         //TODO implementami!!
00686     }
00687     
00688 }

Generato il Thu Jun 23 00:02:59 2005 per JAEBI - BlueTooth J2ME Midlet Client da  doxygen 1.4.3