/* * Copyright 1990-2009 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.jsr082.bluetooth; import java.util.Hashtable; import java.util.Enumeration; import java.util.Vector; import javax.bluetooth.BluetoothStateException; import javax.bluetooth.DiscoveryListener; import javax.bluetooth.RemoteDevice; import javax.bluetooth.UUID; import javax.bluetooth.ServiceRecord; import javax.bluetooth.DataElement; import javax.bluetooth.DeviceClass; import java.io.IOException; public class NativeSDPClient implements ServiceDiscoverer, SDPClient { private Hashtable transactions; String btAddress; RemoteDevice remoteDevice; UUID[] uuidSet = null; /* * Creates a new instance of NativeSDPClient */ public NativeSDPClient() { transactions = new Hashtable(); } /* * Returns an <code>JavaSDPClient<code> object and opens SDP connection * to the remote device with the specified Bluetooth address. * * @param bluetoothAddress bluetooth address of SDP server */ public SDPClient getSDPClient(String bluetoothAddress) { btAddress = bluetoothAddress; remoteDevice = (RemoteDevice)new RemoteDeviceImpl(btAddress); return (SDPClient)this; } /* * Start searching services under the given conditions * * @param attrSet list of attributes whose values are requested. * @param uuidSet list of UUIDs that indicate services relevant to request. * @param btDev remote Bluetooth device to listen response from. * @param discListener discovery listener. * @throws BluetoothStateException */ public int searchService(int[] attrSet, UUID[] uuidSet, RemoteDevice btDev, DiscoveryListener discListener) throws BluetoothStateException { remoteDevice = btDev; btAddress = btDev.getBluetoothAddress(); this.uuidSet = uuidSet; if (discListener == null) { throw new NullPointerException("DiscoveryListener is null"); } SDPTransaction transaction = new SDPTransaction(attrSet, uuidSet, btDev, discListener, this); int nativeHandle = serviceSearchRequest0( btAddress, transaction.getUUIDs(), transaction.getAttributeSet()); if (nativeHandle != 0) { transaction.setId(nativeHandle); synchronized (transactions) { transactions.put(new Integer(nativeHandle), transaction); } BluetoothStack.getEnabledInstance().startPolling(); return nativeHandle; } else { throw new BluetoothStateException("Unable to start service searching"); } } /* * Cancels service discovering * * @param transID ID of a transaction to be canceled * @return true if transaction canceled */ public boolean cancel(int transID) { SDPTransaction transaction; synchronized (transactions) { transaction = (SDPTransaction) transactions. get(new Integer(transID)); } if ((transaction == null) || (transaction.canceled)){ return false; } if (cancelSearchRequest0(transID)) { transaction.canceled = true; return true; } else { return false; } } /* * Initiates ServiceAttribute transaction that retrieves * specified attribute values from a specific service record. */ public void serviceAttributeRequest(int serviceRecordHandle, int[] attrSet, int transactionID, SDPResponseListener listener) throws IOException { SADiscoveryListener sad = new SADiscoveryListener(serviceRecordHandle, attrSet, transactionID, listener); sad.start(); } /* * Initiates ServiceSearchAttribute transaction that searches for services * on a server by UUIDs specified and retrieves values of specified * parameters for service records found. */ public void serviceSearchAttributeRequest(int[] attrSet, UUID[] uuidSet, int transactionID, SDPResponseListener listener) throws IOException{ Boolean synch = new Boolean(true); SSDiscoveryListener ssd = new SSDiscoveryListener(synch); synchronized (synch) { searchService(attrSet, uuidSet, remoteDevice, ssd); try { synch.wait(); } catch (Throwable e) { } } ServiceRecord record = ssd.getServiceRecord(); int[] attrIDs = record.getAttributeIDs(); DataElement[] attrValues = new DataElement[attrIDs.length]; for (int i = 0; i < attrIDs.length; i++) { attrValues[i] = record.getAttributeValue(attrIDs[i]); } listener.serviceSearchAttributeResponse(attrIDs, attrValues, transactionID); } /* * Closes connection of this client to the specified server. * * @throws IOException if no connection is open */ public void close() throws IOException { Enumeration trs = null; synchronized (transactions) { trs = (Enumeration) transactions.keys(); } while (trs.hasMoreElements()) { Object key = trs.nextElement(); int trId = ((Integer)key).intValue(); searchCompleted(trId, DiscoveryListener.SERVICE_SEARCH_TERMINATED); } } /* * Reports service discovering completed * * @param transID ID of a transaction completed * @param result result of searching */ public void searchCompleted(int transID, int result) { SDPTransaction transaction = null; synchronized (transactions) { transaction = (SDPTransaction)transactions. remove(new Integer(transID)); } if (transaction != null) { if (transaction.canceled) { transaction.serviceSearchCompleted( DiscoveryListener.SERVICE_SEARCH_TERMINATED); } else { transaction.serviceSearchCompleted(result); } } } /* * Reports service discovered * * @param transID ID of a transaction completed * @param recHandle handle of the service record */ public void servicesDiscovered(int transID, int recHandle) { SDPTransaction transaction = (SDPTransaction) transactions. get(new Integer(transID)); if ((transaction != null) && !(transaction.canceled)){ transaction.serviceDiscovered(recHandle); } } private native int serviceSearchRequest0(String btAddress, byte [][] UUIDs, int [] attrs); private native boolean cancelSearchRequest0(int transactionID); /* * Service Discovery responce listener that is used within * <code>serviceAttributeRequest()</code> * processing. */ private class SADiscoveryListener implements DiscoveryListener, Runnable { private ServiceRecord sRecord = null; boolean done; int srHandle; int[] attrSet; int transactionID; SDPResponseListener listener; public SADiscoveryListener(int handle, int[] attrSet, int transactionID, SDPResponseListener listener) { done = false; srHandle = handle; this.attrSet = attrSet; this.transactionID = transactionID; this.listener = listener; } public ServiceRecord getServiceRecord() { return sRecord; } /* * Starts this transaction. * * @throws IOException when an I/O error occurs */ public void run() { if (uuidSet == null) { uuidSet = new UUID[0]; } synchronized (this) { try { searchService(attrSet, uuidSet, remoteDevice, this); wait(); } catch (BluetoothStateException bse) { listener.errorResponse(listener.IO_ERROR, bse.getMessage(), transactionID); } catch (Throwable e) { } } ServiceRecord record = getServiceRecord(); if (record == null) { listener.errorResponse(listener.SDP_INVALID_SR_HANDLE, btAddress, transactionID); } else { int numAttr = 0; Vector vAttrIDs = new Vector(); Vector vAttrValues = new Vector(); for (int i = 0; i < attrSet.length; i++) { DataElement attrValue = record.getAttributeValue(attrSet[i]); if (attrValue != null) { vAttrIDs.addElement(new Integer(attrSet[i])); vAttrValues.addElement(attrValue); numAttr++; } } numAttr = vAttrIDs.size(); DataElement[] attrValues = new DataElement[numAttr]; int[] attrIDs = new int[numAttr]; for (int i=0; i < numAttr; i++){ attrIDs[i] = ((Integer)vAttrIDs.elementAt(i)).intValue(); attrValues[i] = (DataElement)vAttrValues.elementAt(i); } listener.serviceAttributeResponse(attrIDs, (numAttr > 0 ? attrValues : null), transactionID); } } public void start() { (new Thread(this)).start(); } public void deviceDiscovered(RemoteDevice btDevice, DeviceClass cod) { throw new RuntimeException("unexpected call"); }; public void servicesDiscovered(int transID, ServiceRecord[] servRecord) { if (done) { return; } int recIndex = -1; for (int i = 0; i < servRecord.length; i++) { if (servRecord[i]. getAttributeValue(ServiceRecordImpl.SERVICE_RECORD_HANDLE). getLong() == srHandle) { recIndex = i; break; } } if (recIndex == -1) { return; } synchronized (this) { try { sRecord = servRecord[recIndex]; done = true; notify(); } catch (Exception e) { e.printStackTrace(); } } }; public void serviceSearchCompleted(int transID, int respCode) { synchronized (this) { try { done = true; notify(); } catch (Exception e) { e.printStackTrace(); } } }; public void inquiryCompleted(int discType) { throw new RuntimeException("unexpected call"); }; } /* * Service Discovery responce listener that is used within * <code>serviceSearchAttributeRequest()</code> * processing. */ private class SSDiscoveryListener implements DiscoveryListener { private Boolean finished; private ServiceRecord sRecord; boolean done; public SSDiscoveryListener(Boolean syn) { finished = syn; done = false; } public ServiceRecord getServiceRecord() { return sRecord; } public void deviceDiscovered(RemoteDevice btDevice, DeviceClass cod) { throw new RuntimeException("unexpected call"); }; public void servicesDiscovered(int transID, ServiceRecord[] servRecord) { if (done) { return; } synchronized (finished) { try { sRecord = servRecord[0]; done = true; finished.notifyAll(); } catch (Exception e) { e.printStackTrace(); } } }; public void serviceSearchCompleted(int transID, int respCode) { return; }; public void inquiryCompleted(int discType) { throw new RuntimeException("unexpected call"); }; } }