Source Code Cross Referenced for BTImageClient.java in  » IDE-Netbeans » mobility » example » bluetooth » demo » Java Source Code / Java DocumentationJava Source Code and Java Documentation

Java Source Code / Java Documentation
1. 6.0 JDK Core
2. 6.0 JDK Modules
3. 6.0 JDK Modules com.sun
4. 6.0 JDK Modules com.sun.java
5. 6.0 JDK Modules sun
6. 6.0 JDK Platform
7. Ajax
8. Apache Harmony Java SE
9. Aspect oriented
10. Authentication Authorization
11. Blogger System
12. Build
13. Byte Code
14. Cache
15. Chart
16. Chat
17. Code Analyzer
18. Collaboration
19. Content Management System
20. Database Client
21. Database DBMS
22. Database JDBC Connection Pool
23. Database ORM
24. Development
25. EJB Server geronimo
26. EJB Server GlassFish
27. EJB Server JBoss 4.2.1
28. EJB Server resin 3.1.5
29. ERP CRM Financial
30. ESB
31. Forum
32. GIS
33. Graphic Library
34. Groupware
35. HTML Parser
36. IDE
37. IDE Eclipse
38. IDE Netbeans
39. Installer
40. Internationalization Localization
41. Inversion of Control
42. Issue Tracking
43. J2EE
44. JBoss
45. JMS
46. JMX
47. Library
48. Mail Clients
49. Net
50. Parser
51. PDF
52. Portal
53. Profiler
54. Project Management
55. Report
56. RSS RDF
57. Rule Engine
58. Science
59. Scripting
60. Search Engine
61. Security
62. Sevlet Container
63. Source Control
64. Swing Library
65. Template Engine
66. Test Coverage
67. Testing
68. UML
69. Web Crawler
70. Web Framework
71. Web Mail
72. Web Server
73. Web Services
74. Web Services apache cxf 2.0.1
75. Web Services AXIS2
76. Wiki Engine
77. Workflow Engines
78. XML
79. XML UI
Java
Java Tutorial
Java Open Source
Jar File Download
Java Articles
Java Products
Java by API
Photoshop Tutorials
Maya Tutorials
Flash Tutorials
3ds-Max Tutorials
Illustrator Tutorials
GIMP Tutorials
C# / C Sharp
C# / CSharp Tutorial
C# / CSharp Open Source
ASP.Net
ASP.NET Tutorial
JavaScript DHTML
JavaScript Tutorial
JavaScript Reference
HTML / CSS
HTML CSS Reference
C / ANSI-C
C Tutorial
C++
C++ Tutorial
Ruby
PHP
Python
Python Tutorial
Python Open Source
SQL Server / T-SQL
SQL Server / T-SQL Tutorial
Oracle PL / SQL
Oracle PL/SQL Tutorial
PostgreSQL
SQL / MySQL
MySQL Tutorial
VB.Net
VB.Net Tutorial
Flash / Flex / ActionScript
VBA / Excel / Access / Word
XML
XML Tutorial
Microsoft Office PowerPoint 2007 Tutorial
Microsoft Office Excel 2007 Tutorial
Microsoft Office Word 2007 Tutorial
Java Source Code / Java Documentation » IDE Netbeans » mobility » example.bluetooth.demo 
Source Cross Referenced  Class Diagram Java Document (Java Doc) 


001:        /*
002:         *
003:         * Copyright (c) 2007, Sun Microsystems, Inc.
004:         *
005:         * All rights reserved.
006:         *
007:         * Redistribution and use in source and binary forms, with or without
008:         * modification, are permitted provided that the following conditions
009:         * are met:
010:         *
011:         *  * Redistributions of source code must retain the above copyright
012:         *    notice, this list of conditions and the following disclaimer.
013:         *  * Redistributions in binary form must reproduce the above copyright
014:         *    notice, this list of conditions and the following disclaimer in the
015:         *    documentation and/or other materials provided with the distribution.
016:         *  * Neither the name of Sun Microsystems nor the names of its contributors
017:         *    may be used to endorse or promote products derived from this software
018:         *    without specific prior written permission.
019:         *
020:         * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
021:         * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
022:         * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
023:         * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
024:         * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
025:         * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
026:         * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
027:         * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
028:         * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
029:         * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
030:         * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
031:         */
032:        package example.bluetooth.demo;
033:
034:        import java.io.IOException;
035:        import java.io.InputStream;
036:        import java.io.OutputStream;
037:
038:        import java.util.Enumeration;
039:        import java.util.Hashtable;
040:        import java.util.Vector;
041:
042:        // jsr082 API
043:        import javax.bluetooth.BluetoothStateException;
044:        import javax.bluetooth.DataElement;
045:        import javax.bluetooth.DeviceClass;
046:        import javax.bluetooth.DiscoveryAgent;
047:        import javax.bluetooth.DiscoveryListener;
048:        import javax.bluetooth.LocalDevice;
049:        import javax.bluetooth.RemoteDevice;
050:        import javax.bluetooth.ServiceRecord;
051:        import javax.bluetooth.UUID;
052:
053:        // midp/cldc API
054:        import javax.microedition.io.Connector;
055:        import javax.microedition.io.StreamConnection;
056:        import javax.microedition.lcdui.Image;
057:
058:        /**
059:         * Initialize BT device, search for BT services,
060:         * presents them to user and picks his/her choice,
061:         * finally download the choosen image and present
062:         * it to user.
063:         *
064:         * @version ,
065:         */
066:        final class BTImageClient implements  Runnable, DiscoveryListener {
067:            /** Describes this server */
068:            private static final UUID PICTURES_SERVER_UUID = new UUID(
069:                    "F0E0D0C0B0A000908070605040302010", false);
070:
071:            /** The attribute id of the record item with images names. */
072:            private static final int IMAGES_NAMES_ATTRIBUTE_ID = 0x4321;
073:
074:            /** Shows the engine is ready to work. */
075:            private static final int READY = 0;
076:
077:            /** Shows the engine is searching bluetooth devices. */
078:            private static final int DEVICE_SEARCH = 1;
079:
080:            /** Shows the engine is searching bluetooth services. */
081:            private static final int SERVICE_SEARCH = 2;
082:
083:            /** Keeps the current state of engine. */
084:            private int state = READY;
085:
086:            /** Keeps the discovery agent reference. */
087:            private DiscoveryAgent discoveryAgent;
088:
089:            /** Keeps the parent reference to process specific actions. */
090:            private GUIImageClient parent;
091:
092:            /** Becomes 'true' when this component is finalized. */
093:            private boolean isClosed;
094:
095:            /** Process the search/download requests. */
096:            private Thread processorThread;
097:
098:            /** Collects the remote devices found during a search. */
099:            private Vector /* RemoteDevice */devices = new Vector();
100:
101:            /** Collects the services found during a search. */
102:            private Vector /* ServiceRecord */records = new Vector();
103:
104:            /** Keeps the device discovery return code. */
105:            private int discType;
106:
107:            /** Keeps the services search IDs (just to be able to cancel them). */
108:            private int[] searchIDs;
109:
110:            /** Keeps the image name to be load. */
111:            private String imageNameToLoad;
112:
113:            /** Keeps the table of {name, Service} to process the user choice. */
114:            private Hashtable base = new Hashtable();
115:
116:            /** Informs the thread the download should be canceled. */
117:            private boolean isDownloadCanceled;
118:
119:            /** Optimization: keeps service search pattern. */
120:            private UUID[] uuidSet;
121:
122:            /** Optimization: keeps attributes list to be retrieved. */
123:            private int[] attrSet;
124:
125:            /**
126:             * Constructs the bluetooth server, but it is initialized
127:             * in the different thread to "avoid dead lock".
128:             */
129:            BTImageClient(GUIImageClient parent) {
130:                this .parent = parent;
131:
132:                // we have to initialize a system in different thread...
133:                processorThread = new Thread(this );
134:                processorThread.start();
135:            }
136:
137:            /**
138:             * Process the search/download requests.
139:             */
140:            public void run() {
141:                // initialize bluetooth first
142:                boolean isBTReady = false;
143:
144:                try {
145:                    // create/get a local device and discovery agent
146:                    LocalDevice localDevice = LocalDevice.getLocalDevice();
147:                    discoveryAgent = localDevice.getDiscoveryAgent();
148:
149:                    // remember we've reached this point.
150:                    isBTReady = true;
151:                } catch (Exception e) {
152:                    System.err.println("Can't initialize bluetooth: " + e);
153:                }
154:
155:                parent.completeInitialization(isBTReady);
156:
157:                // nothing to do if no bluetooth available
158:                if (!isBTReady) {
159:                    return;
160:                }
161:
162:                // initialize some optimization variables
163:                uuidSet = new UUID[2];
164:
165:                // ok, we are interesting in btspp services only
166:                uuidSet[0] = new UUID(0x1101);
167:
168:                // and only known ones, that allows pictures
169:                uuidSet[1] = PICTURES_SERVER_UUID;
170:
171:                // we need an only service attribute actually
172:                attrSet = new int[1];
173:
174:                // it's "images names" one
175:                attrSet[0] = IMAGES_NAMES_ATTRIBUTE_ID;
176:
177:                // start processing the images search/download
178:                processImagesSearchDownload();
179:            }
180:
181:            /**
182:             * Invoked by system when a new remote device is found -
183:             * remember the found device.
184:             */
185:            public void deviceDiscovered(RemoteDevice btDevice, DeviceClass cod) {
186:                // same device may found several times during single search
187:                if (devices.indexOf(btDevice) == -1) {
188:                    devices.addElement(btDevice);
189:                }
190:            }
191:
192:            /**
193:             * Invoked by system when device discovery is done.
194:             * <p>
195:             * Remember the discType
196:             * and process its evaluation in another thread.
197:             */
198:            public void inquiryCompleted(int discType) {
199:                this .discType = discType;
200:
201:                synchronized (this ) {
202:                    notify();
203:                }
204:            }
205:
206:            public void servicesDiscovered(int transID,
207:                    ServiceRecord[] servRecord) {
208:                for (int i = 0; i < servRecord.length; i++) {
209:                    records.addElement(servRecord[i]);
210:                }
211:            }
212:
213:            public void serviceSearchCompleted(int transID, int respCode) {
214:                // first, find the service search transaction index
215:                int index = -1;
216:
217:                for (int i = 0; i < searchIDs.length; i++) {
218:                    if (searchIDs[i] == transID) {
219:                        index = i;
220:
221:                        break;
222:                    }
223:                }
224:
225:                // error - unexpected transaction index
226:                if (index == -1) {
227:                    System.err.println("Unexpected transaction index: "
228:                            + transID);
229:                    // process the error case here
230:                } else {
231:                    searchIDs[index] = -1;
232:                }
233:
234:                /*
235:                 * Actually, we do not care about the response code -
236:                 * if device is not reachable or no records, etc.
237:                 */
238:
239:                // make sure it was the last transaction
240:                for (int i = 0; i < searchIDs.length; i++) {
241:                    if (searchIDs[i] != -1) {
242:                        return;
243:                    }
244:                }
245:
246:                // ok, all of the transactions are completed
247:                synchronized (this ) {
248:                    notify();
249:                }
250:            }
251:
252:            /** Sets the request to search the devices/services. */
253:            void requestSearch() {
254:                synchronized (this ) {
255:                    notify();
256:                }
257:            }
258:
259:            /** Cancel's the devices/services search. */
260:            void cancelSearch() {
261:                synchronized (this ) {
262:                    if (state == DEVICE_SEARCH) {
263:                        discoveryAgent.cancelInquiry(this );
264:                    } else if (state == SERVICE_SEARCH) {
265:                        for (int i = 0; i < searchIDs.length; i++) {
266:                            discoveryAgent.cancelServiceSearch(searchIDs[i]);
267:                        }
268:                    }
269:                }
270:            }
271:
272:            /** Sets the request to load the specified image. */
273:            void requestLoad(String name) {
274:                synchronized (this ) {
275:                    imageNameToLoad = name;
276:                    notify();
277:                }
278:            }
279:
280:            /** Cancel's the image download. */
281:            void cancelLoad() {
282:                /*
283:                 * The image download process is done by
284:                 * this class's thread (not by a system one),
285:                 * so no need to wake up the current thread -
286:                 * it's running already.
287:                 */
288:                isDownloadCanceled = true;
289:            }
290:
291:            /**
292:             * Destroy a work with bluetooth - exits the accepting
293:             * thread and close notifier.
294:             */
295:            void destroy() {
296:                synchronized (this ) {
297:                    isClosed = true;
298:                    isDownloadCanceled = true;
299:                    notify();
300:                }
301:
302:                // wait for acceptor thread is done
303:                try {
304:                    processorThread.join();
305:                } catch (InterruptedException e) {
306:                } // ignore
307:            }
308:
309:            /**
310:             * Processes images search/download until component is closed
311:             * or system error has happen.
312:             */
313:            private synchronized void processImagesSearchDownload() {
314:                while (!isClosed) {
315:                    // wait for new search request from user
316:                    state = READY;
317:
318:                    try {
319:                        wait();
320:                    } catch (InterruptedException e) {
321:                        System.err.println("Unexpected interruption: " + e);
322:
323:                        return;
324:                    }
325:
326:                    // check the component is destroyed
327:                    if (isClosed) {
328:                        return;
329:                    }
330:
331:                    // search for devices
332:                    if (!searchDevices()) {
333:                        return;
334:                    } else if (devices.size() == 0) {
335:                        continue;
336:                    }
337:
338:                    // search for services now
339:                    if (!searchServices()) {
340:                        return;
341:                    } else if (records.size() == 0) {
342:                        continue;
343:                    }
344:
345:                    // ok, something was found - present the result to user now
346:                    if (!presentUserSearchResults()) {
347:                        // services are found, but no names there
348:                        continue;
349:                    }
350:
351:                    // the several download requests may be processed
352:                    while (true) {
353:                        // this download is not canceled, right?
354:                        isDownloadCanceled = false;
355:
356:                        // ok, wait for download or need to wait for next search
357:                        try {
358:                            wait();
359:                        } catch (InterruptedException e) {
360:                            System.err.println("Unexpected interruption: " + e);
361:
362:                            return;
363:                        }
364:
365:                        // check the component is destroyed
366:                        if (isClosed) {
367:                            return;
368:                        }
369:
370:                        // this means "go to the beginning"
371:                        if (imageNameToLoad == null) {
372:                            break;
373:                        }
374:
375:                        // load selected image data
376:                        Image img = loadImage();
377:
378:                        // this should never happen - monitor is taken...
379:                        if (isClosed) {
380:                            return;
381:                        }
382:
383:                        if (isDownloadCanceled) {
384:                            continue; // may be next image to be download
385:                        }
386:
387:                        if (img == null) {
388:                            parent.informLoadError("Can't load image: "
389:                                    + imageNameToLoad);
390:
391:                            continue; // may be next image to be download
392:                        }
393:
394:                        // ok, show image to user
395:                        parent.showImage(img, imageNameToLoad);
396:
397:                        // may be next image to be download
398:                        continue;
399:                    }
400:                }
401:            }
402:
403:            /**
404:             * Search for bluetooth devices.
405:             *
406:             * @return false if should end the component work.
407:             */
408:            private boolean searchDevices() {
409:                // ok, start a new search then
410:                state = DEVICE_SEARCH;
411:                devices.removeAllElements();
412:
413:                try {
414:                    discoveryAgent.startInquiry(DiscoveryAgent.GIAC, this );
415:                } catch (BluetoothStateException e) {
416:                    System.err.println("Can't start inquiry now: " + e);
417:                    parent.informSearchError("Can't start device search");
418:
419:                    return true;
420:                }
421:
422:                try {
423:                    wait(); // until devices are found
424:                } catch (InterruptedException e) {
425:                    System.err.println("Unexpected interruption: " + e);
426:
427:                    return false;
428:                }
429:
430:                // this "wake up" may be caused by 'destroy' call
431:                if (isClosed) {
432:                    return false;
433:                }
434:
435:                // no?, ok, let's check the return code then
436:                switch (discType) {
437:                case INQUIRY_ERROR:
438:                    parent.informSearchError("Device discovering error...");
439:
440:                    // fall through
441:                case INQUIRY_TERMINATED:
442:                    // make sure no garbage in found devices list
443:                    devices.removeAllElements();
444:
445:                    // nothing to report - go to next request
446:                    break;
447:
448:                case INQUIRY_COMPLETED:
449:
450:                    if (devices.size() == 0) {
451:                        parent.informSearchError("No devices in range");
452:                    }
453:
454:                    // go to service search now
455:                    break;
456:
457:                default:
458:                    // what kind of system you are?... :(
459:                    System.err.println("system error:"
460:                            + " unexpected device discovery code: " + discType);
461:                    destroy();
462:
463:                    return false;
464:                }
465:
466:                return true;
467:            }
468:
469:            /**
470:             * Search for proper service.
471:             *
472:             * @return false if should end the component work.
473:             */
474:            private boolean searchServices() {
475:                state = SERVICE_SEARCH;
476:                records.removeAllElements();
477:                searchIDs = new int[devices.size()];
478:
479:                boolean isSearchStarted = false;
480:
481:                for (int i = 0; i < devices.size(); i++) {
482:                    RemoteDevice rd = (RemoteDevice) devices.elementAt(i);
483:
484:                    try {
485:                        searchIDs[i] = discoveryAgent.searchServices(attrSet,
486:                                uuidSet, rd, this );
487:                    } catch (BluetoothStateException e) {
488:                        System.err.println("Can't search services for: "
489:                                + rd.getBluetoothAddress() + " due to " + e);
490:                        searchIDs[i] = -1;
491:
492:                        continue;
493:                    }
494:
495:                    isSearchStarted = true;
496:                }
497:
498:                // at least one of the services search should be found
499:                if (!isSearchStarted) {
500:                    parent.informSearchError("Can't search services.");
501:
502:                    return true;
503:                }
504:
505:                try {
506:                    wait(); // until services are found
507:                } catch (InterruptedException e) {
508:                    System.err.println("Unexpected interruption: " + e);
509:
510:                    return false;
511:                }
512:
513:                // this "wake up" may be caused by 'destroy' call
514:                if (isClosed) {
515:                    return false;
516:                }
517:
518:                // actually, no services were found
519:                if (records.size() == 0) {
520:                    parent.informSearchError("No proper services were found");
521:                }
522:
523:                return true;
524:            }
525:
526:            /**
527:             * Gets the collection of the images titles (names)
528:             * from the services, prepares a hashtable to match
529:             * the image name to a services list, presents the images names
530:             * to user finally.
531:             *
532:             * @return false if no names in found services.
533:             */
534:            private boolean presentUserSearchResults() {
535:                base.clear();
536:
537:                for (int i = 0; i < records.size(); i++) {
538:                    ServiceRecord sr = (ServiceRecord) records.elementAt(i);
539:
540:                    // get the attribute with images names
541:                    DataElement de = sr
542:                            .getAttributeValue(IMAGES_NAMES_ATTRIBUTE_ID);
543:
544:                    if (de == null) {
545:                        System.err
546:                                .println("Unexpected service - missed attribute");
547:
548:                        continue;
549:                    }
550:
551:                    // get the images names from this attribute
552:                    Enumeration deEnum = (Enumeration) de.getValue();
553:
554:                    while (deEnum.hasMoreElements()) {
555:                        de = (DataElement) deEnum.nextElement();
556:
557:                        String name = (String) de.getValue();
558:
559:                        // name may be stored already
560:                        Object obj = base.get(name);
561:
562:                        // that's either the ServiceRecord or Vector
563:                        if (obj != null) {
564:                            Vector v;
565:
566:                            if (obj instanceof  ServiceRecord) {
567:                                v = new Vector();
568:                                v.addElement(obj);
569:                            } else {
570:                                v = (Vector) obj;
571:                            }
572:
573:                            v.addElement(sr);
574:                            obj = v;
575:                        } else {
576:                            obj = sr;
577:                        }
578:
579:                        base.put(name, obj);
580:                    }
581:                }
582:
583:                return parent.showImagesNames(base);
584:            }
585:
586:            /**
587:             * Loads selected image data.
588:             */
589:            private Image loadImage() {
590:                if (imageNameToLoad == null) {
591:                    System.err.println("Error: imageNameToLoad=null");
592:
593:                    return null;
594:                }
595:
596:                // ok, get the list of service records
597:                ServiceRecord[] sr = null;
598:                Object obj = base.get(imageNameToLoad);
599:
600:                if (obj == null) {
601:                    System.err.println("Error: no record for: "
602:                            + imageNameToLoad);
603:
604:                    return null;
605:                } else if (obj instanceof  ServiceRecord) {
606:                    sr = new ServiceRecord[] { (ServiceRecord) obj };
607:                } else {
608:                    Vector v = (Vector) obj;
609:                    sr = new ServiceRecord[v.size()];
610:
611:                    for (int i = 0; i < v.size(); i++) {
612:                        sr[i] = (ServiceRecord) v.elementAt(i);
613:                    }
614:                }
615:
616:                // now try to load the image from each services one by one
617:                for (int i = 0; i < sr.length; i++) {
618:                    StreamConnection conn = null;
619:                    String url = null;
620:
621:                    // the process may be canceled
622:                    if (isDownloadCanceled) {
623:                        return null;
624:                    }
625:
626:                    // first - connect
627:                    try {
628:                        url = sr[i].getConnectionURL(
629:                                ServiceRecord.NOAUTHENTICATE_NOENCRYPT, false);
630:                        conn = (StreamConnection) Connector.open(url);
631:                    } catch (IOException e) {
632:                        System.err.println("Note: can't connect to: " + url);
633:
634:                        // ignore
635:                        continue;
636:                    }
637:
638:                    // then open a steam and write a name
639:                    try {
640:                        OutputStream out = conn.openOutputStream();
641:                        out.write(imageNameToLoad.length()); // length is 1 byte
642:                        out.write(imageNameToLoad.getBytes());
643:                        out.flush();
644:                        out.close();
645:                    } catch (IOException e) {
646:                        System.err.println("Can't write to server for: " + url);
647:
648:                        // close stream connection
649:                        try {
650:                            conn.close();
651:                        } catch (IOException ee) {
652:                        } // ignore
653:
654:                        continue;
655:                    }
656:
657:                    // then open a steam and read an image
658:                    byte[] imgData = null;
659:
660:                    try {
661:                        InputStream in = conn.openInputStream();
662:
663:                        // read a length first
664:                        int length = in.read() << 8;
665:                        length |= in.read();
666:
667:                        if (length <= 0) {
668:                            throw new IOException("Can't read a length");
669:                        }
670:
671:                        // read the image now
672:                        imgData = new byte[length];
673:                        length = 0;
674:
675:                        while (length != imgData.length) {
676:                            int n = in.read(imgData, length, imgData.length
677:                                    - length);
678:
679:                            if (n == -1) {
680:                                throw new IOException("Can't read a image data");
681:                            }
682:
683:                            length += n;
684:                        }
685:
686:                        in.close();
687:                    } catch (IOException e) {
688:                        System.err
689:                                .println("Can't read from server for: " + url);
690:
691:                        continue;
692:                    } finally {
693:                        // close stream connection anyway
694:                        try {
695:                            conn.close();
696:                        } catch (IOException e) {
697:                        } // ignore
698:                    }
699:
700:                    // ok, may it's a chance
701:                    Image img = null;
702:
703:                    try {
704:                        img = Image.createImage(imgData, 0, imgData.length);
705:                    } catch (Exception e) {
706:                        // may be next time
707:                        System.err.println("Error: wrong image data from: "
708:                                + url);
709:
710:                        continue;
711:                    }
712:
713:                    return img;
714:                }
715:
716:                return null;
717:            }
718:        } // end of class 'BTImageClient' definition
www.java2java.com | Contact Us
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.