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.midp.jsr82emul;
027:
028: import java.io.UnsupportedEncodingException;
029: import java.io.IOException;
030: import java.util.Vector;
031:
032: import javax.bluetooth.DiscoveryListener;
033: import javax.bluetooth.DiscoveryAgent;
034:
035: import com.sun.kvem.jsr082.bluetooth.DiscoveryAgentImpl;
036: import com.sun.midp.io.BluetoothUrl;
037: import com.sun.midp.main.Configuration;
038: import com.sun.midp.log.Logging;
039: import com.sun.kvem.jsr082.bluetooth.BCC;
040: import com.sun.midp.jsr082.BluetoothUtils;
041:
042: /**
043: * Emulates a Bluetooth device.
044: */
045: public class DeviceEmul extends EmulationClient implements EmulUnit {
046: /**
047: * Represents and perfoms inquiry.
048: * Constructing an instance starts inquiry in a new thread.
049: */
050: private class Inquiry implements Runnable {
051: /** Access code parameter for the inquiry. */
052: int accessCode;
053:
054: /** Indicates if this inquiry has been cancelled. */
055: private boolean cancelled = false;
056:
057: /** Inquiry thread. */
058: private Thread thread;
059:
060: /**
061: * Constructs an instance and starts corresponding inquiry thread.
062: * @param accessCode access code to be used within the inqiry
063: */
064: Inquiry(int accessCode) {
065: this .accessCode = accessCode;
066: thread = new Thread(this );
067: thread.start();
068: }
069:
070: /** Cancells current inquiry. */
071: synchronized void cancel() {
072: // not interrupting the corresponding thread to let it finish
073: // emulation server communications
074: cancelled = true;
075: }
076:
077: /**
078: * Implements <code>run()</code> of <code>Runnable</code> running
079: * the inquiry
080: */
081: public void run() {
082: try {
083: InquiryResults inquiryResults;
084:
085: synchronized (serverTransaction) {
086: messenger.sendInt(toServer,
087: Messenger.START_INQUIRY, accessCode);
088:
089: // let other threads work while the inquire is processed by
090: // emulation server
091: Thread.yield();
092:
093: messenger.receive(fromServer);
094:
095: if (messenger.getCode() != Messenger.INQUIRY_COMPLETED) {
096: throw new EmulationException();
097: }
098:
099: inquiryResults = new InquiryResults(messenger
100: .getBytes());
101: }
102:
103: byte[][] addresses = inquiryResults.getAddresses();
104: int[] classes = inquiryResults.getClasses();
105:
106: for (int i = 0; i < addresses.length; i++) {
107: if (CheckCancelAndReportDiscovery(addresses[i],
108: classes[i])) {
109: break;
110: }
111: }
112:
113: if (!cancelled) {
114: inquiryCompleted(true);
115: }
116:
117: } catch (Throwable e) {
118: inquiryCompleted(false);
119: }
120:
121: }
122:
123: /**
124: * Checks if inquiry cancelled and reports on device discovery to
125: * listener if it is not.
126: *
127: * @param btaddr BluetoothAddress of device discovered
128: * @param cod class of device discovered
129: * @return <code>true</code> if inquiry is cancelled,
130: * <code>false</code> otherwise
131: */
132: private synchronized boolean CheckCancelAndReportDiscovery(
133: byte[] btaddr, int cod) {
134:
135: if (!cancelled) {
136: deviceDiscovered(btaddr, cod);
137: }
138: return cancelled;
139: }
140: }
141:
142: /** Initial access code. */
143: private static int DEFAULT_AC = DiscoveryAgent.GIAC;
144:
145: /** Keeps current inquiry if any. */
146: private Inquiry curInquiry = null;
147:
148: /** Bluetooth address. */
149: byte[] address = null;
150:
151: /** Device state i.e. discoverable mode and device class. */
152: private DeviceState deviceState = null;
153:
154: /** Lock for emulation server communications that require response. */
155: private Object serverTransaction = new Object();
156:
157: /** Device emulation for the local device. */
158: private static DeviceEmul localDeviceEmul = null;
159:
160: /**
161: * Constructs an emulation instance and retrieves addres for it from
162: * emulation server.
163: */
164: public DeviceEmul() {
165: try {
166: connect();
167:
168: messenger.sendBytes(toServer, Messenger.REGISTER_DEVICE,
169: getLocalIpBytes());
170: messenger.receive(fromServer);
171:
172: if (messenger.getCode() != Messenger.REGISTERED) {
173: throw new IOException(
174: "Error communicating emulation server");
175: }
176:
177: address = messenger.getBytes();
178: int cod = initDevice(address, DEFAULT_AC);
179: deviceState = new DeviceState(cod, DEFAULT_AC);
180: updateState();
181:
182: Log.log("DeviceEmul: my address is "
183: + BluetoothUtils.getAddressString(address));
184: } catch (IOException e) {
185: throw new EmulationException(
186: "Error initializing local device emulation");
187: }
188: }
189:
190: /**
191: * Initializes local device parameters in shared emulation storage.
192: * @param addr Bluetoth address bytes retrieved form emulation server
193: * @param ac initial access code
194: * @return initial class of device with service classes that are possibly
195: * saved after previous usage of device with the same address
196: */
197: private native int initDevice(byte[] addr, int ac);
198:
199: /**
200: * Returns instance of this class for local device emulation.
201: *
202: * @return the device emulation object for the local device
203: */
204: public static synchronized DeviceEmul getLocalDeviceEmul() {
205: if (localDeviceEmul == null) {
206: localDeviceEmul = new DeviceEmul();
207: }
208: return localDeviceEmul;
209: }
210:
211: /**
212: * Retrieves IP address.
213: * @return host computer IP address as byte array.
214: */
215: private byte[] getLocalIpBytes() {
216: String ip = EmulationClient.getLocalIP();
217: byte[] res = null;
218:
219: if (ip != null && ip.length() > 0) {
220: int from = 0;
221: int to = 0;
222: byte[] parsed = new byte[4];
223:
224: try {
225: for (int i = 0; i < 4; i++) {
226: to = ip.indexOf('.', from);
227: if (to < 0) {
228: to = ip.length();
229: }
230: parsed[i] = (byte) Integer.parseInt(ip.substring(
231: from, to));
232: from = to + 1;
233: }
234:
235: res = parsed;
236: } catch (NumberFormatException e) {
237: // res == null idenitifies retrieving failure
238: }
239: }
240:
241: if (res == null) {
242: res = new byte[] { 127, 0, 0, 1 };
243: }
244: return res;
245: }
246:
247: /**
248: * Returns address of this device.
249: * @return Bluetooth address
250: */
251: public byte[] getAddress() {
252: return address;
253: }
254:
255: /**
256: * Registers service at emulation server.
257: * @param serviceData combined service connection info
258: * @throws IOException if connection to emulation server failed.
259: */
260: void registerService(ServiceConnectionData serviceData)
261: throws IOException {
262:
263: synchronized (serverTransaction) {
264: messenger
265: .sendBytes(
266: toServer,
267: Messenger.REGISTER_SERVICE,
268: serviceData
269: .toByteArray(ServiceConnectionData.SERVER_DATA));
270: }
271: }
272:
273: /**
274: * Unregisters service at emulation server.
275: * @param serverSocketPort socket port desired service
276: * accepted connections at
277: */
278: void unregisterService(int serverSocketPort) {
279: try {
280: messenger.sendInt(toServer, Messenger.UNREGISTER_SERVICE,
281: serverSocketPort);
282: } catch (IOException e) {
283: if (Logging.TRACE_ENABLED) {
284: Logging.trace(e, "Unregistering service failed");
285: }
286: }
287: }
288:
289: /** Sends device state update to emulation server. */
290: private void updateState() {
291: try {
292: messenger.sendInt(toServer, Messenger.UPDATE_DEVICE_STATE,
293: deviceState.toInt());
294: } catch (IOException e) {
295: throw new EmulationException(e.getMessage());
296: }
297: }
298:
299: /**
300: * Starts inquiry.
301: * @param accessCode access code of desired devices
302: */
303: private synchronized void startInquiry(int accessCode) {
304: curInquiry = new Inquiry(accessCode);
305: }
306:
307: /**
308: * Cancels current inquiry.
309: */
310: private synchronized void cancelInquiry() {
311: if (curInquiry != null) {
312: curInquiry.cancel();
313: }
314: }
315:
316: /**
317: * Notifies on device discovery.
318: * @param addr Bluetooth address of device discovered
319: * @param cod class of device discovered
320: */
321: private native void deviceDiscovered(byte[] addr, int cod);
322:
323: /**
324: * Notifies on inquiry completion.
325: * @param success true if completed successfully, false otherwize
326: */
327: private native void inquiryCompleted(boolean success);
328:
329: /** Request code for updating service classes. */
330: static final int UPDATE_CLASS = 0;
331: /** Request code for updating access code. */
332: static final int UPDATE_ACCESS = 1;
333: /** Request code for updating starting inquiry. */
334: static final int START_INQUIRY = 2;
335: /** Request code for cancelling inquiry. */
336: static final int CANCEL_INQUIRY = 3;
337: /** Request code for initing devie. */
338: static final int INIT_DEVICE = 4;
339:
340: /**
341: * Processes the request.
342: * @param request Packeted request
343: */
344: public void process(BytePack request) {
345: switch (request.extract()) {
346: case UPDATE_CLASS:
347: Log.log("Processing UPDATE_CLASS");
348: deviceState.setServiceClasses(request.extractInt());
349: updateState();
350: break;
351: case UPDATE_ACCESS:
352: Log.log("Processing UPDATE_ACCESS");
353: deviceState.setDiscoverable(request.extractInt());
354: updateState();
355: break;
356: case START_INQUIRY:
357: Log.log("Processing START_INQUIRY");
358: startInquiry(request.extractInt());
359: break;
360: case CANCEL_INQUIRY:
361: Log.log("Processing CANCEL_INQUIRY");
362: cancelInquiry();
363: break;
364: case INIT_DEVICE:
365: Log.log("Processing INIT_DEVICE");
366: // Nothing to do: the request has already caused
367: // construction of all the objects required.
368: break;
369: }
370: }
371:
372: /**
373: * Saves device information in persistent storage for future use.
374: */
375: private native void finalize();
376: }
377:
378: /**
379: * Utility class that allows packing inquiry results in bytes array.
380: */
381: class InquiryResults {
382: /** Amount of devices discovered. */
383: int count = 0;
384:
385: /** Size of Bluetooth address byte representation. */
386: private static final int ADDRESS_SIZE = Const.BTADDR_SIZE;
387: /** Size of device class byte representation. */
388: private static final int COD_SIZE = 3;
389: /** Size of one result. */
390: private static final int RESULT_SIZE = ADDRESS_SIZE + COD_SIZE;
391:
392: /** Addresses of discovered devices if not <code>null</code>. */
393: private byte[][] addresses;
394: /** Classes of discovered devices if not <code>null</code>. */
395: private int[] classes = null;
396:
397: /** Inquiry results: Bluetooth address, device class pairs. */
398: Vector results;
399:
400: /** Constructs an instance for filling up. */
401: InquiryResults() {
402: results = new Vector();
403: }
404:
405: /**
406: * Constructs an instance by byte representation and unpacks it to
407: * normal addresses and device classes.
408: *
409: * @param data byte representation of inquiry results.
410: */
411: InquiryResults(byte[] data) {
412: if (data == null || (data.length % RESULT_SIZE) != 0) {
413: throw new IllegalArgumentException();
414: }
415:
416: count = data.length / RESULT_SIZE;
417: addresses = new byte[count][ADDRESS_SIZE];
418: classes = new int[count];
419:
420: for (int i = 0; i < count; i++) {
421: int j = i * RESULT_SIZE;
422:
423: classes[i] = (data[j++] & 0xff) | ((data[j++] & 0xff) << 8)
424: | ((data[j++] & 0xff) << 16);
425:
426: System.arraycopy(data, j, addresses[i], 0, ADDRESS_SIZE);
427: }
428: }
429:
430: /**
431: * Returns classes of discovered devices.
432: * @return int array that contains classes of discovered devices
433: */
434: int[] getClasses() {
435: if (classes == null) {
436: throw new IllegalArgumentException();
437: }
438: return classes;
439: }
440:
441: /**
442: * Returns addresses of discovered devices
443: * @return array of bluetooth addresses, byte representation.
444: */
445: byte[][] getAddresses() {
446: if (addresses == null) {
447: throw new IllegalArgumentException();
448: }
449: return addresses;
450: }
451:
452: /**
453: * Adds new device discovered to results.
454: * @param btaddr Bluetooth address of device discovered
455: * @param cod class of device discovered
456: */
457: void add(byte[] btaddr, int cod) {
458: byte[] bytes = new byte[RESULT_SIZE];
459:
460: bytes[0] = (byte) (cod & 0xff);
461: bytes[1] = (byte) ((cod >> 8) & 0xff);
462: bytes[2] = (byte) ((cod >> 16) & 0xff);
463:
464: System.arraycopy(btaddr, 0, bytes, COD_SIZE, ADDRESS_SIZE);
465:
466: results.addElement(bytes);
467: }
468:
469: /**
470: * Packs results to byte representation.
471: * @return byte array that represent current results
472: */
473: byte[] toByteArray() {
474: count = results.size();
475: byte[] data = new byte[count * RESULT_SIZE];
476:
477: for (int i = 0; i < count; i++) {
478: System.arraycopy((byte[]) results.elementAt(i), 0, data, i
479: * RESULT_SIZE, RESULT_SIZE);
480: }
481:
482: return data;
483: }
484: }
485:
486: /**
487: * Utility class that allows packing device class and discoverable mode into
488: * single integer.
489: */
490: class DeviceState {
491: /** Packed information. */
492: private int data = 0;
493:
494: /** Mask for highlighting device class. */
495: private static final int DEVICE_CLASS = 0x1ffc;
496: /** Mask for highlighting device class. */
497: private static final int SERV_CLASSES = 0xffe000;
498: /**
499: * Mask for highlighting entire device class including device and
500: * service classes.
501: */
502: private static final int COD = DEVICE_CLASS | SERV_CLASSES;
503: /** Mask for highlighting a bit that shows device discoverable mode. */
504: private static final int DISCOVERABLE = 0xff000000;
505: /** LIAC bit. */
506: private static final int LIAC = 0x01000000;
507: /** GIAC bit. */
508: private static final int GIAC = 0x02000000;
509: /** Uniscoverable. */
510: private static final int UNDISCOVERABLE = 0;
511:
512: /**
513: * Constructs an instance with given value.
514: * @param cod value for class of device
515: * @param mode value for discoverable mode
516: */
517: DeviceState(int cod, int mode) {
518: data = cod;
519: setDiscoverable(mode);
520: }
521:
522: /**
523: * Constructs default state that is undiscoverable and has invalid
524: * device class.
525: */
526: DeviceState() {
527: this .data = UNDISCOVERABLE;
528: }
529:
530: /**
531: * Retrieves integer representation.
532: * @return integer representation
533: */
534: int toInt() {
535: return data;
536: }
537:
538: /**
539: * Retrieves entire class of device including device and service classes.
540: * @return integer value that represents class of device.
541: */
542: int getCoD() {
543: return data & COD;
544: }
545:
546: /**
547: * Returns discoverable mode.
548: * @return integer that represents discoverable mode.
549: */
550: int getDiscoverable() {
551: switch (data & DISCOVERABLE) {
552: case LIAC:
553: return DiscoveryAgent.LIAC;
554: case GIAC:
555: return DiscoveryAgent.GIAC;
556: default:
557: return DiscoveryAgent.NOT_DISCOVERABLE;
558: }
559: }
560:
561: /**
562: * Sets service classes of device.
563: * @param classes new service classes value
564: */
565: void setServiceClasses(int classes) {
566: data = data & (DISCOVERABLE | DEVICE_CLASS) | classes;
567: }
568:
569: /**
570: * Sets discoverable mode to given one.
571: * @param mode discoverable mode to set to
572: * @return true if the discoverable mode is supported, otherwise - false
573: */
574: boolean setDiscoverable(int mode) {
575: int bits = 0;
576: boolean ret = true;
577: switch (mode) {
578: case DiscoveryAgent.LIAC:
579: bits = LIAC;
580: break;
581: case DiscoveryAgent.GIAC:
582: bits = GIAC;
583: break;
584: case DiscoveryAgent.NOT_DISCOVERABLE:
585: bits = UNDISCOVERABLE;
586: break;
587: default:
588: ret = false;
589: break;
590: }
591: if (ret) {
592: data = bits | data & DEVICE_CLASS;
593: }
594: return ret;
595: }
596:
597: /**
598: * Updates values from new integer representation.
599: * @param data new integer representation
600: */
601: void update(int data) {
602: this.data = data;
603: }
604: }
|