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.kvem.jsr082.obex.SessionNotifierImpl;
029: import javax.microedition.io.Connection;
030: import javax.bluetooth.DeviceClass;
031: import javax.bluetooth.DiscoveryAgent;
032: import javax.bluetooth.BluetoothStateException;
033: import javax.bluetooth.ServiceRecord;
034: import javax.bluetooth.ServiceRegistrationException;
035:
036: /**
037: * The <code>LocalDeviceImpl</code> class is a LocalDevice
038: * API class implementation. This is a singleton class.
039: */
040: public final class LocalDeviceImpl {
041:
042: /** Set to false in RR version - then the javac skip the code. */
043: private static final boolean DEBUG = false;
044:
045: /** Keeps this singleton object. */
046: private static LocalDeviceImpl instance;
047:
048: /** Keeps bluetooth address of this device. */
049: private String bluetoothAddress;
050:
051: /** Timeout canceller of limited discoverable mode. */
052: private CancelerOfLIAC cancelerOfLIAC = new CancelerOfLIAC();
053:
054: /**
055: * Device should not be in LIAC for more than 1 minute,
056: * then return to the previous mode.
057: */
058: private class CancelerOfLIAC implements Runnable {
059: /** One minute. */
060: private long MINUTE = 60000;
061: /** Specifies the delay for timeout checks. */
062: private int RETRY_DELAY = 100; // ms
063: /** Saved access code to get back to at timeout. */
064: private int savedCode;
065: /** Keeps canceller start time to check if timeout expired. */
066: private long startTime = -1;
067: /** Flaggs if LIAC mode has been cancelled from outside. */
068: private boolean isCanceledFromOutside = true;
069:
070: /**
071: * Starts timeout killer if new discoverable mode is LIAC.
072: *
073: * @param oldCode the previous value of discoverable mode.
074: * @param newCode discoverable mode that has been set just.
075: */
076: synchronized void notifyNewAccessCode(int oldCode, int newCode) {
077: if (newCode == oldCode) {
078: return;
079: }
080: savedCode = oldCode;
081:
082: if (newCode == DiscoveryAgent.LIAC) {
083: // the currentCode was not LIAC - start a killer
084: startTime = System.currentTimeMillis();
085: new Thread(this ).start();
086: } else {
087: /*
088: * startTime != -1 if the killer is running, but
089: * this method may be called by the killer itself -
090: * then there is no need to stop it.
091: */
092: boolean stopKiller = startTime != -1
093: && isCanceledFromOutside;
094: startTime = -1;
095: isCanceledFromOutside = false;
096:
097: if (stopKiller) {
098: try {
099: wait();
100: } catch (InterruptedException e) {
101: }
102: }
103: }
104: }
105:
106: /**
107: * Implements of <code>run()</code> of <code>Runnable</code> interface.
108: */
109: public void run() {
110: while (true) {
111: try {
112: Thread.sleep(RETRY_DELAY);
113: } catch (InterruptedException e) {
114: } // ignore
115:
116: synchronized (this ) {
117: // the access code was changed by application
118: if (startTime == -1) {
119: notify();
120: return;
121: }
122: // minute is running yet
123: if (System.currentTimeMillis() - startTime < MINUTE) {
124: continue;
125: }
126: // minute is over - change the mode back
127: isCanceledFromOutside = false;
128: boolean res = false;
129:
130: try {
131: res = setDiscoverable(savedCode);
132: } catch (BluetoothStateException e) {
133: }
134:
135: if (!res) {
136: // not now - h-m-m, ok, try later then
137: isCanceledFromOutside = true;
138: continue;
139: }
140: return;
141: }
142: }
143: }
144: } // end of class 'CancelerOfLIAC' definition
145:
146: /**
147: * Constructs the only instance of <code>LocalDeviceImpl</code>.
148: */
149: private LocalDeviceImpl() {
150: }
151:
152: /**
153: * Retrieves singleton instance.
154: *
155: * @return the only instance of <code>LocalDeviceImpl</code>
156: * @throws BluetoothStateException if an error occured.
157: */
158: public static synchronized LocalDeviceImpl getInstance()
159: throws BluetoothStateException {
160: if (instance == null) {
161: instance = new LocalDeviceImpl();
162: }
163: return instance;
164: }
165:
166: // JAVADOC COMMENT ELIDED
167: public String getFriendlyName() {
168: return BCC.getInstance().getFriendlyName();
169: }
170:
171: // JAVADOC COMMENT ELIDED
172: public DeviceClass getDeviceClass() {
173: return BCC.getInstance().getDeviceClass();
174: }
175:
176: // JAVADOC COMMENT ELIDED
177: public String getProperty(String property) {
178: return System.getProperty(property);
179: }
180:
181: // JAVADOC COMMENT ELIDED
182: public int getDiscoverable() {
183: return BCC.getInstance().getAccessCode();
184: }
185:
186: // JAVADOC COMMENT ELIDED
187: public String getBluetoothAddress() {
188: return BCC.getInstance().getBluetoothAddress();
189: }
190:
191: // JAVADOC COMMENT ELIDED
192: public boolean setDiscoverable(int accessCode)
193: throws BluetoothStateException {
194: // Check if the specified mode has a valid value
195: if (accessCode != DiscoveryAgent.GIAC
196: && accessCode != DiscoveryAgent.LIAC
197: && accessCode != DiscoveryAgent.NOT_DISCOVERABLE
198: && (accessCode < 0x9E8B00 || accessCode > 0x9E8B3F)) {
199: throw new IllegalArgumentException(
200: "Access code is out of range: "
201: + "0x"
202: + Integer.toHexString(accessCode)
203: .toUpperCase());
204: }
205: synchronized (cancelerOfLIAC) {
206: /*
207: * Accroding to the spec, the device should only be limited
208: * discoverable (DiscoveryAgent.LIAC) for 1 minute -
209: * then back to the PREVIOUS discoverable mode.
210: */
211: int oldAccessCode = BCC.getInstance().getAccessCode();
212: if (BCC.getInstance().setAccessCode(accessCode)) {
213: cancelerOfLIAC.notifyNewAccessCode(oldAccessCode,
214: accessCode);
215: if (accessCode != DiscoveryAgent.NOT_DISCOVERABLE) {
216: // Start SDDB if discoverable mode was set successfully
217: // IMPL_NOTE: Do we really need this step?
218: SDDB.getInstance();
219: }
220: return true;
221: }
222: }
223: return false;
224: }
225:
226: // JAVADOC COMMENT ELIDED
227: public ServiceRecord getRecord(Connection notifier) {
228: if (notifier == null) {
229: throw new NullPointerException("Null notifier specified.");
230: }
231: if (!(notifier instanceof BluetoothNotifier)) {
232: if (!(notifier instanceof SessionNotifierImpl)) {
233: throw new IllegalArgumentException(
234: "Invalid notifier class.");
235: }
236: Connection transport = ((SessionNotifierImpl) notifier)
237: .getTransport();
238: if (!(transport instanceof BluetoothNotifier)) {
239: throw new IllegalArgumentException(
240: "Invalid notifier class.");
241: }
242: return ((BluetoothNotifier) transport).getServiceRecord();
243: }
244: return ((BluetoothNotifier) notifier).getServiceRecord();
245: }
246:
247: // JAVADOC COMMENT ELIDED
248: public void updateRecord(ServiceRecord srvRecord)
249: throws ServiceRegistrationException {
250: if (DEBUG) {
251: System.out.println("LocalDeviceImpl.updateRecord");
252: }
253: if (srvRecord == null) {
254: throw new NullPointerException("Null record specified.");
255: }
256: if (!(srvRecord instanceof ServiceRecordImpl)) {
257: throw new IllegalArgumentException(
258: "Invalid service record class.");
259: }
260: ServiceRecordImpl record = (ServiceRecordImpl) srvRecord;
261: BluetoothNotifier notifier = record.getNotifier();
262: if (notifier == null) {
263: throw new IllegalArgumentException(
264: "Service record is not from local SDDB.");
265: }
266: notifier.updateServiceRecord(record);
267: }
268:
269: /**
270: * Checks if Bluetooth device is turned on.
271: *
272: * @return <code>true</code> is the Bluetooth device is on,
273: * <code>false</code> otherwise.
274: */
275: public boolean isPowerOn() {
276: return BCC.getInstance().isBluetoothEnabled();
277: }
278:
279: }
|