/*
*
*
* Copyright 1990-2007 Sun Microsystems, Inc. All Rights Reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License version
* 2 only, as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License version 2 for more details (a copy is
* included at /legal/license.txt).
*
* You should have received a copy of the GNU General Public License
* version 2 along with this work; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA
*
* Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
* Clara, CA 95054 or visit www.sun.com if you need additional
* information or have any questions.
*/
package com.sun.kvem.jsr082.bluetooth;
import com.sun.kvem.jsr082.obex.SessionNotifierImpl;
import javax.microedition.io.Connection;
import javax.bluetooth.DeviceClass;
import javax.bluetooth.DiscoveryAgent;
import javax.bluetooth.BluetoothStateException;
import javax.bluetooth.ServiceRecord;
import javax.bluetooth.ServiceRegistrationException;
/**
* The <code>LocalDeviceImpl</code> class is a LocalDevice
* API class implementation. This is a singleton class.
*/
public final class LocalDeviceImpl {
/** Set to false in RR version - then the javac skip the code. */
private static final boolean DEBUG = false;
/** Keeps this singleton object. */
private static LocalDeviceImpl instance;
/** Keeps bluetooth address of this device. */
private String bluetoothAddress;
/** Timeout canceller of limited discoverable mode. */
private CancelerOfLIAC cancelerOfLIAC = new CancelerOfLIAC();
/**
* Device should not be in LIAC for more than 1 minute,
* then return to the previous mode.
*/
private class CancelerOfLIAC implements Runnable {
/** One minute. */
private long MINUTE = 60000;
/** Specifies the delay for timeout checks. */
private int RETRY_DELAY = 100; // ms
/** Saved access code to get back to at timeout. */
private int savedCode;
/** Keeps canceller start time to check if timeout expired. */
private long startTime = -1;
/** Flaggs if LIAC mode has been cancelled from outside. */
private boolean isCanceledFromOutside = true;
/**
* Starts timeout killer if new discoverable mode is LIAC.
*
* @param oldCode the previous value of discoverable mode.
* @param newCode discoverable mode that has been set just.
*/
synchronized void notifyNewAccessCode(int oldCode, int newCode) {
if (newCode == oldCode) {
return;
}
savedCode = oldCode;
if (newCode == DiscoveryAgent.LIAC) {
// the currentCode was not LIAC - start a killer
startTime = System.currentTimeMillis();
new Thread(this).start();
} else {
/*
* startTime != -1 if the killer is running, but
* this method may be called by the killer itself -
* then there is no need to stop it.
*/
boolean stopKiller = startTime != -1 && isCanceledFromOutside;
startTime = -1;
isCanceledFromOutside = false;
if (stopKiller) {
try {
wait();
} catch (InterruptedException e) {}
}
}
}
/**
* Implements of <code>run()</code> of <code>Runnable</code> interface.
*/
public void run() {
while (true) {
try {
Thread.sleep(RETRY_DELAY);
} catch (InterruptedException e) {} // ignore
synchronized (this) {
// the access code was changed by application
if (startTime == -1) {
notify();
return;
}
// minute is running yet
if (System.currentTimeMillis() - startTime < MINUTE) {
continue;
}
// minute is over - change the mode back
isCanceledFromOutside = false;
boolean res = false;
try {
res = setDiscoverable(savedCode);
} catch (BluetoothStateException e) {}
if (!res) {
// not now - h-m-m, ok, try later then
isCanceledFromOutside = true;
continue;
}
return;
}
}
}
} // end of class 'CancelerOfLIAC' definition
/**
* Constructs the only instance of <code>LocalDeviceImpl</code>.
*/
private LocalDeviceImpl() {
}
/**
* Retrieves singleton instance.
*
* @return the only instance of <code>LocalDeviceImpl</code>
* @throws BluetoothStateException if an error occured.
*/
public static synchronized LocalDeviceImpl getInstance()
throws BluetoothStateException {
if (instance == null) {
instance = new LocalDeviceImpl();
}
return instance;
}
// JAVADOC COMMENT ELIDED
public String getFriendlyName() {
return BCC.getInstance().getFriendlyName();
}
// JAVADOC COMMENT ELIDED
public DeviceClass getDeviceClass() {
return BCC.getInstance().getDeviceClass();
}
// JAVADOC COMMENT ELIDED
public String getProperty(String property) {
return System.getProperty(property);
}
// JAVADOC COMMENT ELIDED
public int getDiscoverable() {
return BCC.getInstance().getAccessCode();
}
// JAVADOC COMMENT ELIDED
public String getBluetoothAddress() {
return BCC.getInstance().getBluetoothAddress();
}
// JAVADOC COMMENT ELIDED
public boolean setDiscoverable(int accessCode)
throws BluetoothStateException {
// Check if the specified mode has a valid value
if (accessCode != DiscoveryAgent.GIAC &&
accessCode != DiscoveryAgent.LIAC &&
accessCode != DiscoveryAgent.NOT_DISCOVERABLE &&
(accessCode < 0x9E8B00 || accessCode > 0x9E8B3F)) {
throw new IllegalArgumentException("Access code is out of range: "
+ "0x" + Integer.toHexString(accessCode).toUpperCase());
}
synchronized (cancelerOfLIAC) {
/*
* Accroding to the spec, the device should only be limited
* discoverable (DiscoveryAgent.LIAC) for 1 minute -
* then back to the PREVIOUS discoverable mode.
*/
int oldAccessCode = BCC.getInstance().getAccessCode();
if (BCC.getInstance().setAccessCode(accessCode)) {
cancelerOfLIAC.notifyNewAccessCode(oldAccessCode, accessCode);
if (accessCode != DiscoveryAgent.NOT_DISCOVERABLE) {
// Start SDDB if discoverable mode was set successfully
// IMPL_NOTE: Do we really need this step?
SDDB.getInstance();
}
return true;
}
}
return false;
}
// JAVADOC COMMENT ELIDED
public ServiceRecord getRecord(Connection notifier) {
if (notifier == null) {
throw new NullPointerException("Null notifier specified.");
}
if (!(notifier instanceof BluetoothNotifier)) {
if (!(notifier instanceof SessionNotifierImpl)) {
throw new IllegalArgumentException("Invalid notifier class.");
}
Connection transport =
((SessionNotifierImpl)notifier).getTransport();
if (!(transport instanceof BluetoothNotifier)) {
throw new IllegalArgumentException("Invalid notifier class.");
}
return ((BluetoothNotifier)transport).getServiceRecord();
}
return ((BluetoothNotifier)notifier).getServiceRecord();
}
// JAVADOC COMMENT ELIDED
public void updateRecord(ServiceRecord srvRecord)
throws ServiceRegistrationException {
if (DEBUG) {
System.out.println("LocalDeviceImpl.updateRecord");
}
if (srvRecord == null) {
throw new NullPointerException("Null record specified.");
}
if (!(srvRecord instanceof ServiceRecordImpl)) {
throw new IllegalArgumentException("Invalid service record class.");
}
ServiceRecordImpl record = (ServiceRecordImpl)srvRecord;
BluetoothNotifier notifier = record.getNotifier();
if (notifier == null) {
throw new IllegalArgumentException(
"Service record is not from local SDDB.");
}
notifier.updateServiceRecord(record);
}
/**
* Checks if Bluetooth device is turned on.
*
* @return <code>true</code> is the Bluetooth device is on,
* <code>false</code> otherwise.
*/
public boolean isPowerOn() {
return BCC.getInstance().isBluetoothEnabled();
}
}
|