001: /*
002: *
003: *
004: * Copyright 1990-2007 Sun Microsystems, Inc. All Rights Reserved.
005: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER
006: *
007: * This program is free software; you can redistribute it and/or
008: * modify it under the terms of the GNU General Public License version
009: * 2 only, as published by the Free Software Foundation.
010: *
011: * This program is distributed in the hope that it will be useful, but
012: * WITHOUT ANY WARRANTY; without even the implied warranty of
013: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
014: * General Public License version 2 for more details (a copy is
015: * included at /legal/license.txt).
016: *
017: * You should have received a copy of the GNU General Public License
018: * version 2 along with this work; if not, write to the Free Software
019: * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
020: * 02110-1301 USA
021: *
022: * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
023: * Clara, CA 95054 or visit www.sun.com if you need additional
024: * information or have any questions.
025: */
026: package com.sun.kvem.jsr082.bluetooth;
027:
028: import com.sun.midp.jsr082.bluetooth.BluetoothStack;
029: import javax.bluetooth.BluetoothStateException;
030: import javax.bluetooth.DiscoveryAgent;
031: import javax.bluetooth.DiscoveryListener;
032: import javax.bluetooth.RemoteDevice;
033: import javax.bluetooth.UUID;
034: import java.util.Enumeration;
035: import java.util.Hashtable;
036: import java.util.Vector;
037:
038: /**
039: * The <code>DiscoveryAgentImpl</code> class is a DiscoveryAgent
040: * API class implementation which does not extend this API class.
041: */
042: public final class DiscoveryAgentImpl {
043: /** Calls inquiry completion callback in a separate thread. */
044: class Completed implements Runnable {
045: /** type of completion. */
046: private int discType;
047: /** listener to be called. */
048: private DiscoveryListener listener;
049:
050: /** Constructs an instance and starts the the thread. */
051: Completed(DiscoveryListener listener, int discType) {
052: this .listener = listener;
053: this .discType = discType;
054: new Thread(this ).start();
055: }
056:
057: /** Implements Runnable. */
058: public void run() {
059: listener.inquiryCompleted(discType);
060: }
061: }
062:
063: /* Set to false in RR version - then the javac skip the code. */
064: private static final boolean DEBUG = false;
065:
066: /**
067: * Keeps an instance to the object of this class to be
068: * accessible from the implementation.
069: */
070: private static DiscoveryAgentImpl instance;
071:
072: /** Keeps the <code>RemoteDeviceImpl</code> references of known devices. */
073: private Hashtable knownDevices = new Hashtable();
074:
075: /** Keeps the <code>RemoteDeviceImpl</code> references of cached devices. */
076: private Hashtable cachedDevices = new Hashtable();
077:
078: /**
079: * Keeps the listener of the device discovery inquire.
080: * Also, it is used as flag that a device is in inquire mode.
081: */
082: private DiscoveryListener d_listener;
083:
084: /** Keeps the lock object for device discovery synchronization. */
085: private Object d_lock = new Object();
086:
087: /** Keeps the reference to module responsible for selecting services. */
088: private SelectServiceHandler selectServiceHandler = new SelectServiceHandler(
089: this );
090:
091: /** Constructs the single instance. */
092: private DiscoveryAgentImpl() {
093: }
094:
095: // JAVADOC COMMENT ELIDED
096: public RemoteDevice[] retrieveDevices(int option) {
097: switch (option) {
098: case DiscoveryAgent.CACHED:
099: // IMPL_NOTE: use native cache keeping addresses of found devices
100: // to share the cache between multiple isolates
101: return getCachedDevices();
102: case DiscoveryAgent.PREKNOWN:
103: Vector pk = BCC.getInstance().getPreknownDevices();
104: if (pk == null || pk.size() == 0) {
105: return null;
106: }
107: RemoteDevice[] res = new RemoteDevice[pk.size()];
108: for (int i = 0; i < pk.size(); i++) {
109: String addr = (String) pk.elementAt(i);
110: res[i] = getRemoteDevice(addr);
111: }
112: return res;
113: default:
114: throw new IllegalArgumentException("Invalid option value: "
115: + option);
116: }
117: }
118:
119: /**
120: * Adds address of remote device found during inquiry request to internal
121: * inquiry cache.
122: *
123: * The method does nothing if the RemoteDevice is already in the cache.
124: */
125: public void addCachedDevice(String addr) {
126: RemoteDevice rd = getRemoteDevice(addr);
127: synchronized (cachedDevices) {
128: cachedDevices.put(addr, rd);
129: }
130: }
131:
132: // JAVADOC COMMENT ELIDED
133: private RemoteDevice[] getCachedDevices() {
134: synchronized (cachedDevices) {
135: int len = cachedDevices.size();
136: if (len == 0) {
137: return null;
138: }
139: RemoteDevice[] res = new RemoteDevice[len];
140: Enumeration e = cachedDevices.elements();
141: for (int i = 0; e.hasMoreElements(); i++) {
142: res[i] = (RemoteDevice) e.nextElement();
143: }
144: return res;
145: }
146: }
147:
148: // JAVADOC COMMENT ELIDED
149: public boolean startInquiry(int accessCode,
150: DiscoveryListener listener) throws BluetoothStateException {
151:
152: if (accessCode != DiscoveryAgent.GIAC
153: && accessCode != DiscoveryAgent.LIAC
154: && (accessCode < 0x9E8B00 || accessCode > 0x9E8B3F)) {
155: throw new IllegalArgumentException(
156: "Access code is out of range: " + accessCode);
157: }
158:
159: if (listener == null) {
160: throw new NullPointerException("null listener");
161: }
162:
163: // IMPL_NOTE see
164: // kvem/classes/com/sun/kvem/jsr082/impl/bluetooth/
165: // BTDeviceDiscoverer.java
166: // heck what access codes should be supported.
167: // Return false if access code is not supported.
168:
169: synchronized (d_lock) {
170: if (d_listener != null) {
171: throw new BluetoothStateException(
172: "The previous device discovery is running...");
173: }
174: d_listener = listener;
175:
176: // process the inquiry in the device specific way
177: return startInquiry(accessCode);
178: }
179: }
180:
181: private boolean startInquiry(int accessCode)
182: throws BluetoothStateException {
183: return BluetoothStack.getEnabledInstance().startInquiry(
184: accessCode, d_listener);
185: }
186:
187: // JAVADOC COMMENT ELIDED
188: public boolean cancelInquiry(DiscoveryListener listener) {
189: if (listener == null) {
190: throw new NullPointerException("null listener");
191: }
192: synchronized (d_lock) {
193:
194: // no inquiry was started
195: if (d_listener == null) {
196: return false;
197: }
198:
199: // not valid listener
200: if (d_listener != listener) {
201: return false;
202: }
203:
204: // process the inquiry in the device specific way
205: cancelInquiry();
206: }
207:
208: inquiryCompleted(DiscoveryListener.INQUIRY_TERMINATED);
209: return true;
210: }
211:
212: /**
213: * Cancels inquiry in device specific way.
214: */
215: private void cancelInquiry() {
216: BluetoothStack.getInstance().cancelInquiry(d_listener);
217: }
218:
219: /**
220: * Porting interface: this method is used by the device specific
221: * implementation to notify this class, that the current inquire
222: * has been completed.
223: *
224: * @param discType type of completion:
225: * <code>DiscoveryListener.INQUIRY_COMPLETED</code>, or
226: * <code>DiscoveryListener.INQUIRY_TERMINATED</code>, or
227: * <code>DiscoveryListener.INQUIRY_ERROR</code>
228: */
229: public void inquiryCompleted(int discType) {
230: DiscoveryListener listener;
231: synchronized (d_lock) {
232: listener = d_listener;
233: d_listener = null;
234: }
235:
236: new Completed(listener, discType);
237: }
238:
239: /**
240: * Porting interface: this method is used by the device specific
241: * implementation to create the RemoteDevice object by address.
242: *
243: * Also, this method puts the new remote devices into cache of
244: * known devices.
245: *
246: * @param addr address of remote device to be created
247: *
248: * @return new <code>RemoteDeviceImpl</code>instance if device with address
249: * given is unknown, the known one otherwise.
250: */
251: public RemoteDeviceImpl getRemoteDevice(String addr) {
252: synchronized (knownDevices) {
253: addr = addr.toUpperCase();
254: RemoteDeviceImpl rd = (RemoteDeviceImpl) knownDevices
255: .get(addr);
256:
257: if (rd == null) {
258: rd = new RemoteDeviceImpl(addr);
259: knownDevices.put(addr, rd);
260: }
261: return rd;
262: }
263: }
264:
265: // JAVADOC COMMENT ELIDED
266: public int searchServices(int[] attrSet, UUID[] uuidSet,
267: RemoteDevice btDev, DiscoveryListener discListener)
268: throws BluetoothStateException {
269:
270: if (DEBUG) {
271: System.out.println("searchServices: ");
272: System.out.println("\tattrSet=" + attrSet);
273:
274: if (attrSet != null) {
275: for (int i = 0; i < attrSet.length; i++) {
276: System.out.println("\tattrSet[" + i + "]=0x"
277: + attrSet[i]);
278: }
279: }
280: System.out.println("\tuuidSet=" + uuidSet);
281:
282: if (uuidSet != null) {
283: for (int i = 0; i < uuidSet.length; i++) {
284: System.out.println("\tuuidSet[" + i + "]="
285: + uuidSet[i]);
286: }
287: }
288: System.out.println("\tadderess="
289: + btDev.getBluetoothAddress());
290: }
291: ServiceSearcher searcher = new ServiceSearcher(attrSet,
292: uuidSet, btDev, discListener);
293:
294: // the 'transID' is assigned by service discoverer (searcher)
295: int transID = searcher.start();
296:
297: if (DEBUG) {
298: System.out.println("\ttransID=" + transID);
299: }
300: return transID;
301: }
302:
303: // JAVADOC COMMENT ELIDED
304: public boolean cancelServiceSearch(int transID) {
305: if (DEBUG) {
306: System.out.println("cancelServiceSearch: transID="
307: + transID);
308: }
309: return ServiceSearcher.cancel(transID);
310: }
311:
312: // JAVADOC COMMENT ELIDED
313: public String selectService(UUID uuid, int security, boolean master)
314: throws BluetoothStateException {
315:
316: // use the separated class to light this one
317: return selectServiceHandler.selectService(uuid, security,
318: master);
319: }
320:
321: /**
322: * Returns the instance of this singleton constructing it if needed.
323: * @return the only instance of <code>DiscoveryAgentImpl</code>.
324: */
325: public static synchronized DiscoveryAgentImpl getInstance() {
326: if (instance == null) {
327: instance = new DiscoveryAgentImpl();
328: }
329:
330: return instance;
331: }
332: } // end of class 'DiscoveryAgentImpl' definition
|