diff options
author | Jaikumar Ganesh <jaikumar@google.com> | 2009-05-05 22:26:12 -0700 |
---|---|---|
committer | Jaikumar Ganesh <jaikumar@google.com> | 2009-06-09 17:21:08 -0700 |
commit | d5ac1ae36b4e096eb97984334f86d0c68abea2f7 (patch) | |
tree | 0e9c8b664eee7bce22f49a17dab4993292c17e1d /core/java | |
parent | b099c4699b8d32295caa7b59637657d47a7c7486 (diff) | |
download | frameworks_base-d5ac1ae36b4e096eb97984334f86d0c68abea2f7.zip frameworks_base-d5ac1ae36b4e096eb97984334f86d0c68abea2f7.tar.gz frameworks_base-d5ac1ae36b4e096eb97984334f86d0c68abea2f7.tar.bz2 |
Framework changes for bluez4.
Changes in the Bluetooth JNI calls and framework functions
for Bluez4.
Diffstat (limited to 'core/java')
-rw-r--r-- | core/java/android/bluetooth/BluetoothDevice.java | 192 | ||||
-rw-r--r-- | core/java/android/bluetooth/BluetoothUuid.java | 62 | ||||
-rw-r--r-- | core/java/android/bluetooth/IBluetoothDevice.aidl | 26 | ||||
-rw-r--r-- | core/java/android/bluetooth/IBluetoothDeviceCallback.aidl | 25 | ||||
-rw-r--r-- | core/java/android/server/BluetoothA2dpService.java | 403 | ||||
-rw-r--r-- | core/java/android/server/BluetoothDeviceService.java | 698 | ||||
-rw-r--r-- | core/java/android/server/BluetoothEventLoop.java | 266 |
7 files changed, 687 insertions, 985 deletions
diff --git a/core/java/android/bluetooth/BluetoothDevice.java b/core/java/android/bluetooth/BluetoothDevice.java index 951b4b0..c942a27 100644 --- a/core/java/android/bluetooth/BluetoothDevice.java +++ b/core/java/android/bluetooth/BluetoothDevice.java @@ -75,7 +75,7 @@ public class BluetoothDevice { public static final int UNBOND_REASON_REMOVED = 6; private static final String TAG = "BluetoothDevice"; - + private final IBluetoothDevice mService; /** * @hide - hide this because it takes a parameter of type @@ -180,31 +180,6 @@ public class BluetoothDevice { return false; } - public String getVersion() { - try { - return mService.getVersion(); - } catch (RemoteException e) {Log.e(TAG, "", e);} - return null; - } - public String getRevision() { - try { - return mService.getRevision(); - } catch (RemoteException e) {Log.e(TAG, "", e);} - return null; - } - public String getManufacturer() { - try { - return mService.getManufacturer(); - } catch (RemoteException e) {Log.e(TAG, "", e);} - return null; - } - public String getCompany() { - try { - return mService.getCompany(); - } catch (RemoteException e) {Log.e(TAG, "", e);} - return null; - } - /** * Get the current scan mode. * Used to determine if the local device is connectable and/or discoverable @@ -241,11 +216,8 @@ public class BluetoothDevice { } public boolean startDiscovery() { - return startDiscovery(true); - } - public boolean startDiscovery(boolean resolveNames) { try { - return mService.startDiscovery(resolveNames); + return mService.startDiscovery(); } catch (RemoteException e) {Log.e(TAG, "", e);} return false; } @@ -263,88 +235,17 @@ public class BluetoothDevice { return false; } - public boolean startPeriodicDiscovery() { - try { - return mService.startPeriodicDiscovery(); - } catch (RemoteException e) {Log.e(TAG, "", e);} - return false; - } - public boolean stopPeriodicDiscovery() { - try { - return mService.stopPeriodicDiscovery(); - } catch (RemoteException e) {Log.e(TAG, "", e);} - return false; - } - public boolean isPeriodicDiscovery() { - try { - return mService.isPeriodicDiscovery(); - } catch (RemoteException e) {Log.e(TAG, "", e);} - return false; - } - - public String[] listRemoteDevices() { - try { - return mService.listRemoteDevices(); - } catch (RemoteException e) {Log.e(TAG, "", e);} - return null; - } - - /** - * List remote devices that have a low level (ACL) connection. - * - * RFCOMM, SDP and L2CAP are all built on ACL connections. Devices can have - * an ACL connection even when not paired - this is common for SDP queries - * or for in-progress pairing requests. - * - * In most cases you probably want to test if a higher level protocol is - * connected, rather than testing ACL connections. - * - * @return bluetooth hardware addresses of remote devices with a current - * ACL connection. Array size is 0 if no devices have a - * connection. Null on error. - */ - public String[] listAclConnections() { - try { - return mService.listAclConnections(); - } catch (RemoteException e) {Log.e(TAG, "", e);} - return null; - } - - /** - * Check if a specified remote device has a low level (ACL) connection. - * - * RFCOMM, SDP and L2CAP are all built on ACL connections. Devices can have - * an ACL connection even when not paired - this is common for SDP queries - * or for in-progress pairing requests. - * - * In most cases you probably want to test if a higher level protocol is - * connected, rather than testing ACL connections. - * - * @param address the Bluetooth hardware address you want to check. - * @return true if there is an ACL connection, false otherwise and on - * error. - */ - public boolean isAclConnected(String address) { - try { - return mService.isAclConnected(address); - } catch (RemoteException e) {Log.e(TAG, "", e);} - return false; - } - /** - * Perform a low level (ACL) disconnection of a remote device. - * - * This forcably disconnects the ACL layer connection to a remote device, - * which will cause all RFCOMM, SDP and L2CAP connections to this remote - * device to close. + * Removes the remote device and the pairing information associated + * with it. * * @param address the Bluetooth hardware address you want to disconnect. * @return true if the device was disconnected, false otherwise and on * error. */ - public boolean disconnectRemoteDeviceAcl(String address) { + public boolean removeBond(String address) { try { - return mService.disconnectRemoteDeviceAcl(address); + return mService.removeBond(address); } catch (RemoteException e) {Log.e(TAG, "", e);} return false; } @@ -377,16 +278,6 @@ public class BluetoothDevice { } /** - * Remove an already exisiting bonding (delete the link key). - */ - public boolean removeBond(String address) { - try { - return mService.removeBond(address); - } catch (RemoteException e) {Log.e(TAG, "", e);} - return false; - } - - /** * List remote devices that are bonded (paired) to the local device. * * Bonding (pairing) is the process by which the user enters a pin code for @@ -440,78 +331,25 @@ public class BluetoothDevice { return null; } - public String getRemoteVersion(String address) { - try { - return mService.getRemoteVersion(address); - } catch (RemoteException e) {Log.e(TAG, "", e);} - return null; - } - public String getRemoteRevision(String address) { - try { - return mService.getRemoteRevision(address); - } catch (RemoteException e) {Log.e(TAG, "", e);} - return null; - } - public String getRemoteManufacturer(String address) { - try { - return mService.getRemoteManufacturer(address); - } catch (RemoteException e) {Log.e(TAG, "", e);} - return null; - } - public String getRemoteCompany(String address) { - try { - return mService.getRemoteCompany(address); - } catch (RemoteException e) {Log.e(TAG, "", e);} - return null; - } - - /** - * Returns the RFCOMM channel associated with the 16-byte UUID on - * the remote Bluetooth address. - * - * Performs a SDP ServiceSearchAttributeRequest transaction. The provided - * uuid is verified in the returned record. If there was a problem, or the - * specified uuid does not exist, -1 is returned. - */ - public boolean getRemoteServiceChannel(String address, short uuid16, - IBluetoothDeviceCallback callback) { - try { - return mService.getRemoteServiceChannel(address, uuid16, callback); - } catch (RemoteException e) {Log.e(TAG, "", e);} - return false; - } - - /** - * Get the major, minor and servics classes of a remote device. - * These classes are encoded as a 32-bit integer. See BluetoothClass. - * @param address remote device - * @return 32-bit class suitable for use with BluetoothClass, or - * BluetoothClass.ERROR on error - */ public int getRemoteClass(String address) { try { return mService.getRemoteClass(address); } catch (RemoteException e) {Log.e(TAG, "", e);} - return BluetoothClass.ERROR; + return BluetoothError.ERROR_IPC; } - public byte[] getRemoteFeatures(String address) { + public String[] getRemoteUuids(String address) { try { - return mService.getRemoteFeatures(address); + return mService.getRemoteUuids(address); } catch (RemoteException e) {Log.e(TAG, "", e);} return null; } - public String lastSeen(String address) { - try { - return mService.lastSeen(address); - } catch (RemoteException e) {Log.e(TAG, "", e);} - return null; - } - public String lastUsed(String address) { - try { - return mService.lastUsed(address); - } catch (RemoteException e) {Log.e(TAG, "", e);} - return null; + + public int getRemoteServiceChannel(String address, String uuid) { + try { + return mService.getRemoteServiceChannel(address, uuid); + } catch (RemoteException e) {Log.e(TAG, "", e);} + return BluetoothError.ERROR_IPC; } public boolean setPin(String address, byte[] pin) { diff --git a/core/java/android/bluetooth/BluetoothUuid.java b/core/java/android/bluetooth/BluetoothUuid.java new file mode 100644 index 0000000..96b93f9 --- /dev/null +++ b/core/java/android/bluetooth/BluetoothUuid.java @@ -0,0 +1,62 @@ +/* + * Copyright (C) 2009 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.bluetooth; + +import java.util.UUID; + +/** +* Static helper methods and constants to decode the UUID of remote devices. +* @hide +*/ +public final class BluetoothUuid { + + /* See Bluetooth Assigned Numbers document - SDP section, to get the values of UUIDs + * for the various services. + * + * The following 128 bit values are calculated as: + * uuid * 2^96 + BASE_UUID + */ + public static final UUID AudioSink = UUID.fromString("0000110A-0000-1000-8000-00805F9B34FB"); + public static final UUID AudioSource = UUID.fromString("0000110B-0000-1000-8000-00805F9B34FB"); + public static final UUID AdvAudioDist = UUID.fromString("0000110D-0000-1000-8000-00805F9B34FB"); + public static final UUID HSP = UUID.fromString("00001108-0000-1000-8000-00805F9B34FB"); + public static final UUID HeadsetHS = UUID.fromString("00001131-0000-1000-8000-00805F9B34FB"); + public static final UUID Handsfree = UUID.fromString("0000111e-0000-1000-8000-00805F9B34FB"); + public static final UUID HandsfreeAudioGateway + = UUID.fromString("0000111f-0000-1000-8000-00805F9B34FB"); + + public static boolean isAudioSource(UUID uuid) { + return uuid.equals(AudioSource); + } + + public static boolean isAudioSink(UUID uuid) { + return uuid.equals(AudioSink); + } + + public static boolean isAdvAudioDist(UUID uuid) { + return uuid.equals(AdvAudioDist); + } + + public static boolean isHandsfree(UUID uuid) { + return uuid.equals(Handsfree) || uuid.equals(HandsfreeAudioGateway); + } + + public static boolean isHeadset(UUID uuid) { + return uuid.equals(HSP) || uuid.equals(HeadsetHS); + } +} + diff --git a/core/java/android/bluetooth/IBluetoothDevice.aidl b/core/java/android/bluetooth/IBluetoothDevice.aidl index 6cd792e..c249c81 100644 --- a/core/java/android/bluetooth/IBluetoothDevice.aidl +++ b/core/java/android/bluetooth/IBluetoothDevice.aidl @@ -16,8 +16,6 @@ package android.bluetooth; -import android.bluetooth.IBluetoothDeviceCallback; - /** * System private API for talking with the Bluetooth service. * @@ -33,10 +31,6 @@ interface IBluetoothDevice String getAddress(); String getName(); boolean setName(in String name); - String getVersion(); - String getRevision(); - String getManufacturer(); - String getCompany(); int getScanMode(); boolean setScanMode(int mode); @@ -44,17 +38,9 @@ interface IBluetoothDevice int getDiscoverableTimeout(); boolean setDiscoverableTimeout(int timeout); - boolean startDiscovery(boolean resolveNames); + boolean startDiscovery(); boolean cancelDiscovery(); boolean isDiscovering(); - boolean startPeriodicDiscovery(); - boolean stopPeriodicDiscovery(); - boolean isPeriodicDiscovery(); - String[] listRemoteDevices(); - - String[] listAclConnections(); - boolean isAclConnected(in String address); - boolean disconnectRemoteDeviceAcl(in String address); boolean createBond(in String address); boolean cancelBondProcess(in String address); @@ -63,15 +49,9 @@ interface IBluetoothDevice int getBondState(in String address); String getRemoteName(in String address); - String getRemoteVersion(in String address); - String getRemoteRevision(in String address); int getRemoteClass(in String address); - String getRemoteManufacturer(in String address); - String getRemoteCompany(in String address); - boolean getRemoteServiceChannel(in String address, int uuid16, in IBluetoothDeviceCallback callback); - byte[] getRemoteFeatures(in String adddress); - String lastSeen(in String address); - String lastUsed(in String address); + String[] getRemoteUuids(in String address); + int getRemoteServiceChannel(in String address, String uuid); boolean setPin(in String address, in byte[] pin); boolean cancelPin(in String address); diff --git a/core/java/android/bluetooth/IBluetoothDeviceCallback.aidl b/core/java/android/bluetooth/IBluetoothDeviceCallback.aidl deleted file mode 100644 index d057093..0000000 --- a/core/java/android/bluetooth/IBluetoothDeviceCallback.aidl +++ /dev/null @@ -1,25 +0,0 @@ -/* - * Copyright (C) 2008, The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package android.bluetooth; - -/** - * {@hide} - */ -oneway interface IBluetoothDeviceCallback -{ - void onGetRemoteServiceChannelResult(in String address, int channel); -} diff --git a/core/java/android/server/BluetoothA2dpService.java b/core/java/android/server/BluetoothA2dpService.java index 5c4e56d..1cf7be9 100644 --- a/core/java/android/server/BluetoothA2dpService.java +++ b/core/java/android/server/BluetoothA2dpService.java @@ -26,14 +26,13 @@ import android.bluetooth.BluetoothA2dp; import android.bluetooth.BluetoothDevice; import android.bluetooth.BluetoothError; import android.bluetooth.BluetoothIntent; +import android.bluetooth.BluetoothUuid; import android.bluetooth.IBluetoothA2dp; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; -import android.content.pm.PackageManager; import android.media.AudioManager; -import android.os.Binder; import android.os.Handler; import android.os.Message; import android.provider.Settings; @@ -44,6 +43,7 @@ import java.io.PrintWriter; import java.util.ArrayList; import java.util.HashMap; import java.util.List; +import java.util.UUID; public class BluetoothA2dpService extends IBluetoothA2dp.Stub { private static final String TAG = "BluetoothA2dpService"; @@ -58,57 +58,22 @@ public class BluetoothA2dpService extends IBluetoothA2dp.Stub { private static final String BLUETOOTH_ENABLED = "bluetooth_enabled"; private static final int MESSAGE_CONNECT_TO = 1; - private static final int MESSAGE_DISCONNECT = 2; - private final Context mContext; - private final IntentFilter mIntentFilter; - private HashMap<String, SinkState> mAudioDevices; - private final AudioManager mAudioManager; - private final BluetoothDevice mBluetooth; + private static final String PROPERTY_STATE = "State"; - // list of disconnected sinks to process after a delay - private final ArrayList<String> mPendingDisconnects = new ArrayList<String>(); - // number of active sinks - private int mSinkCount = 0; + private static final String SINK_STATE_DISCONNECTED = "disconnected"; + private static final String SINK_STATE_CONNECTING = "connecting"; + private static final String SINK_STATE_CONNECTED = "connected"; + private static final String SINK_STATE_PLAYING = "playing"; - private class SinkState { - public String address; - public int state; - public SinkState(String a, int s) {address = a; state = s;} - } + private static int mSinkCount; - public BluetoothA2dpService(Context context) { - mContext = context; - mAudioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE); - - mBluetooth = (BluetoothDevice) mContext.getSystemService(Context.BLUETOOTH_SERVICE); - if (mBluetooth == null) { - throw new RuntimeException("Platform does not support Bluetooth"); - } - - if (!initNative()) { - throw new RuntimeException("Could not init BluetoothA2dpService"); - } - - mIntentFilter = new IntentFilter(BluetoothIntent.BLUETOOTH_STATE_CHANGED_ACTION); - mIntentFilter.addAction(BluetoothIntent.BOND_STATE_CHANGED_ACTION); - mIntentFilter.addAction(BluetoothIntent.REMOTE_DEVICE_CONNECTED_ACTION); - mContext.registerReceiver(mReceiver, mIntentFilter); - - if (mBluetooth.isEnabled()) { - onBluetoothEnable(); - } - } - - @Override - protected void finalize() throws Throwable { - try { - cleanupNative(); - } finally { - super.finalize(); - } - } + private final Context mContext; + private final IntentFilter mIntentFilter; + private HashMap<String, Integer> mAudioDevices; + private final AudioManager mAudioManager; + private final BluetoothDeviceService mBluetoothService; private final BroadcastReceiver mReceiver = new BroadcastReceiver() { @Override @@ -151,6 +116,40 @@ public class BluetoothA2dpService extends IBluetoothA2dp.Stub { } }; + public BluetoothA2dpService(Context context, BluetoothDeviceService bluetoothService) { + mContext = context; + + mAudioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE); + + mBluetoothService = bluetoothService; + if (mBluetoothService == null) { + throw new RuntimeException("Platform does not support Bluetooth"); + } + + if (!initNative()) { + throw new RuntimeException("Could not init BluetoothA2dpService"); + } + + mIntentFilter = new IntentFilter(BluetoothIntent.BLUETOOTH_STATE_CHANGED_ACTION); + mIntentFilter.addAction(BluetoothIntent.BOND_STATE_CHANGED_ACTION); + mIntentFilter.addAction(BluetoothIntent.REMOTE_DEVICE_CONNECTED_ACTION); + mContext.registerReceiver(mReceiver, mIntentFilter); + + mAudioDevices = new HashMap<String, Integer>(); + + if (mBluetoothService.isEnabled()) + onBluetoothEnable(); + } + + @Override + protected void finalize() throws Throwable { + try { + cleanupNative(); + } finally { + super.finalize(); + } + } + private final Handler mHandler = new Handler() { @Override public void handleMessage(Message msg) { @@ -159,7 +158,7 @@ public class BluetoothA2dpService extends IBluetoothA2dp.Stub { String address = (String)msg.obj; // check bluetooth is still on, device is still preferred, and // nothing is currently connected - if (mBluetooth.isEnabled() && + if (mBluetoothService.isEnabled() && getSinkPriority(address) > BluetoothA2dp.PRIORITY_OFF && lookupSinksMatchingStates(new int[] { BluetoothA2dp.STATE_CONNECTING, @@ -170,45 +169,85 @@ public class BluetoothA2dpService extends IBluetoothA2dp.Stub { connectSink(address); } break; - case MESSAGE_DISCONNECT: - handleDeferredDisconnect((String)msg.obj); - break; } } }; + private int convertBluezSinkStringtoState(String value) { + if (value.equalsIgnoreCase("disconnected")) + return BluetoothA2dp.STATE_DISCONNECTED; + if (value.equalsIgnoreCase("connecting")) + return BluetoothA2dp.STATE_CONNECTING; + if (value.equalsIgnoreCase("connected")) + return BluetoothA2dp.STATE_CONNECTED; + if (value.equalsIgnoreCase("playing")) + return BluetoothA2dp.STATE_PLAYING; + return -1; + } + + private synchronized boolean addAudioSink (String address) { + String path = mBluetoothService.getObjectPathFromAddress(address); + String propValues[] = (String []) getSinkPropertiesNative(path); + if (propValues == null) { + Log.e(TAG, "Error while getting AudioSink properties for device: " + address); + return false; + } + Integer state = null; + // Properties are name-value pairs + for (int i = 0; i < propValues.length; i+=2) { + if (propValues[i].equals(PROPERTY_STATE)) { + state = new Integer(convertBluezSinkStringtoState(propValues[i+1])); + break; + } + } + mAudioDevices.put(address, state); + handleSinkStateChange(address, BluetoothA2dp.STATE_DISCONNECTED, state); + return true; + } + private synchronized void onBluetoothEnable() { - mAudioDevices = new HashMap<String, SinkState>(); - String[] paths = (String[])listHeadsetsNative(); - if (paths != null) { - for (String path : paths) { - mAudioDevices.put(path, new SinkState(getAddressNative(path), - isSinkConnectedNative(path) ? BluetoothA2dp.STATE_CONNECTED : - BluetoothA2dp.STATE_DISCONNECTED)); + String devices = mBluetoothService.getProperty("Devices"); + mSinkCount = 0; + if (devices != null) { + String [] paths = devices.split(","); + for (String path: paths) { + String address = mBluetoothService.getAddressFromObjectPath(path); + String []uuids = mBluetoothService.getRemoteUuids(address); + if (uuids != null) + for (String uuid: uuids) { + UUID remoteUuid = UUID.fromString(uuid); + if (BluetoothUuid.isAudioSink(remoteUuid) || + BluetoothUuid.isAudioSource(remoteUuid) || + BluetoothUuid.isAdvAudioDist(remoteUuid)) { + addAudioSink(address); + break; + } + } } } mAudioManager.setParameter(BLUETOOTH_ENABLED, "true"); } private synchronized void onBluetoothDisable() { - if (mAudioDevices != null) { - // copy to allow modification during iteration - String[] paths = new String[mAudioDevices.size()]; - paths = mAudioDevices.keySet().toArray(paths); - for (String path : paths) { - switch (mAudioDevices.get(path).state) { + if (!mAudioDevices.isEmpty()) { + String [] addresses = new String[mAudioDevices.size()]; + addresses = mAudioDevices.keySet().toArray(addresses); + for (String address : addresses) { + int state = getSinkState(address); + switch (state) { case BluetoothA2dp.STATE_CONNECTING: case BluetoothA2dp.STATE_CONNECTED: case BluetoothA2dp.STATE_PLAYING: - disconnectSinkNative(path); - updateState(path, BluetoothA2dp.STATE_DISCONNECTED); + disconnectSinkNative(mBluetoothService.getObjectPathFromAddress(address)); + handleSinkStateChange(address,state, BluetoothA2dp.STATE_DISCONNECTED); break; case BluetoothA2dp.STATE_DISCONNECTING: - updateState(path, BluetoothA2dp.STATE_DISCONNECTED); + handleSinkStateChange(address, BluetoothA2dp.STATE_DISCONNECTING, + BluetoothA2dp.STATE_DISCONNECTED); break; } } - mAudioDevices = null; + mAudioDevices.clear(); } mAudioManager.setBluetoothA2dpOn(false); mAudioManager.setParameter(BLUETOOTH_ENABLED, "false"); @@ -221,9 +260,7 @@ public class BluetoothA2dpService extends IBluetoothA2dp.Stub { if (!BluetoothDevice.checkBluetoothAddress(address)) { return BluetoothError.ERROR; } - if (mAudioDevices == null) { - return BluetoothError.ERROR; - } + // ignore if there are any active sinks if (lookupSinksMatchingStates(new int[] { BluetoothA2dp.STATE_CONNECTING, @@ -233,20 +270,11 @@ public class BluetoothA2dpService extends IBluetoothA2dp.Stub { return BluetoothError.ERROR; } - String path = lookupPath(address); - if (path == null) { - path = createHeadsetNative(address); - if (DBG) log("new bluez sink: " + address + " (" + path + ")"); - } - if (path == null) { + if (mAudioDevices.get(address) == null && !addAudioSink(address)) return BluetoothError.ERROR; - } - SinkState sink = mAudioDevices.get(path); - int state = BluetoothA2dp.STATE_DISCONNECTED; - if (sink != null) { - state = sink.state; - } + int state = mAudioDevices.get(address); + switch (state) { case BluetoothA2dp.STATE_CONNECTED: case BluetoothA2dp.STATE_PLAYING: @@ -256,11 +284,14 @@ public class BluetoothA2dpService extends IBluetoothA2dp.Stub { return BluetoothError.SUCCESS; } + String path = mBluetoothService.getObjectPathFromAddress(address); + if (path == null) + return BluetoothError.ERROR; + // State is DISCONNECTED if (!connectSinkNative(path)) { return BluetoothError.ERROR; } - updateState(path, BluetoothA2dp.STATE_CONNECTING); return BluetoothError.SUCCESS; } @@ -271,14 +302,12 @@ public class BluetoothA2dpService extends IBluetoothA2dp.Stub { if (!BluetoothDevice.checkBluetoothAddress(address)) { return BluetoothError.ERROR; } - if (mAudioDevices == null) { - return BluetoothError.ERROR; - } - String path = lookupPath(address); + String path = mBluetoothService.getObjectPathFromAddress(address); if (path == null) { return BluetoothError.ERROR; } - switch (mAudioDevices.get(path).state) { + + switch (getSinkState(address)) { case BluetoothA2dp.STATE_DISCONNECTED: return BluetoothError.ERROR; case BluetoothA2dp.STATE_DISCONNECTING: @@ -289,7 +318,6 @@ public class BluetoothA2dpService extends IBluetoothA2dp.Stub { if (!disconnectSinkNative(path)) { return BluetoothError.ERROR; } else { - updateState(path, BluetoothA2dp.STATE_DISCONNECTING); return BluetoothError.SUCCESS; } } @@ -305,15 +333,10 @@ public class BluetoothA2dpService extends IBluetoothA2dp.Stub { if (!BluetoothDevice.checkBluetoothAddress(address)) { return BluetoothError.ERROR; } - if (mAudioDevices == null) { + Integer state = mAudioDevices.get(address); + if (state == null) return BluetoothA2dp.STATE_DISCONNECTED; - } - for (SinkState sink : mAudioDevices.values()) { - if (address.equals(sink.address)) { - return sink.state; - } - } - return BluetoothA2dp.STATE_DISCONNECTED; + return state; } public synchronized int getSinkPriority(String address) { @@ -337,103 +360,68 @@ public class BluetoothA2dpService extends IBluetoothA2dp.Stub { BluetoothError.SUCCESS : BluetoothError.ERROR; } - private synchronized void onHeadsetCreated(String path) { - updateState(path, BluetoothA2dp.STATE_DISCONNECTED); - } - - private synchronized void onHeadsetRemoved(String path) { - if (mAudioDevices == null) return; - mAudioDevices.remove(path); - } - - private synchronized void onSinkConnected(String path) { - // if we are reconnected, do not process previous disconnect event. - mPendingDisconnects.remove(path); - - if (mAudioDevices == null) return; - // bluez 3.36 quietly disconnects the previous sink when a new sink - // is connected, so we need to mark all previously connected sinks as - // disconnected - - // copy to allow modification during iteration - String[] paths = new String[mAudioDevices.size()]; - paths = mAudioDevices.keySet().toArray(paths); - for (String oldPath : paths) { - if (path.equals(oldPath)) { - continue; - } - int state = mAudioDevices.get(oldPath).state; - if (state == BluetoothA2dp.STATE_CONNECTED || state == BluetoothA2dp.STATE_PLAYING) { - updateState(path, BluetoothA2dp.STATE_DISCONNECTED); - } + private synchronized void onSinkPropertyChanged(String path, String []propValues) { + String name = propValues[0]; + String address = mBluetoothService.getAddressFromObjectPath(path); + if (address == null) { + Log.e(TAG, "onSinkPropertyChanged: Address of the remote device in null"); + return; } - updateState(path, BluetoothA2dp.STATE_CONNECTING); - mAudioManager.setParameter(A2DP_SINK_ADDRESS, lookupAddress(path)); - mAudioManager.setBluetoothA2dpOn(true); - updateState(path, BluetoothA2dp.STATE_CONNECTED); - } - - private synchronized void onSinkDisconnected(String path) { - // This is to work around a problem in bluez that results - // sink disconnect events being sent, immediately followed by a reconnect. - // To avoid unnecessary audio routing changes, we defer handling - // sink disconnects until after a short delay. - mPendingDisconnects.add(path); - Message msg = Message.obtain(mHandler, MESSAGE_DISCONNECT, path); - mHandler.sendMessageDelayed(msg, 2000); - } - - private synchronized void handleDeferredDisconnect(String path) { - if (mPendingDisconnects.contains(path)) { - mPendingDisconnects.remove(path); - if (mSinkCount == 1) { - mAudioManager.setBluetoothA2dpOn(false); - } - updateState(path, BluetoothA2dp.STATE_DISCONNECTED); + if (mAudioDevices.get(address) == null) { + // Ignore this state change, since it means we have got it after + // bluetooth has been disabled. + return; + } + if (name.equals(PROPERTY_STATE)) { + int state = convertBluezSinkStringtoState(propValues[1]); + int prevState = mAudioDevices.get(address); + handleSinkStateChange(address, prevState, state); } } - private synchronized void onSinkPlaying(String path) { - updateState(path, BluetoothA2dp.STATE_PLAYING); - } - - private synchronized void onSinkStopped(String path) { - updateState(path, BluetoothA2dp.STATE_CONNECTED); - } + private void handleSinkStateChange(String address, int prevState, int state) { + if (state != prevState) { + if (state == BluetoothA2dp.STATE_DISCONNECTED || + state == BluetoothA2dp.STATE_DISCONNECTING) { + if (prevState == BluetoothA2dp.STATE_CONNECTED || + prevState == BluetoothA2dp.STATE_PLAYING) { + // disconnecting or disconnected + Intent intent = new Intent(AudioManager.ACTION_AUDIO_BECOMING_NOISY); + mContext.sendBroadcast(intent); + } + if (--mSinkCount == 0) + mAudioManager.setBluetoothA2dpOn(false); + } else if (state == BluetoothA2dp.STATE_CONNECTED) { + mSinkCount ++; + } + mAudioDevices.put(address, state); - private synchronized final String lookupAddress(String path) { - if (mAudioDevices == null) return null; - SinkState sink = mAudioDevices.get(path); - if (sink == null) { - Log.w(TAG, "lookupAddress() called for unknown device " + path); - updateState(path, BluetoothA2dp.STATE_DISCONNECTED); - } - String address = mAudioDevices.get(path).address; - if (address == null) Log.e(TAG, "Can't find address for " + path); - return address; - } + Intent intent = new Intent(BluetoothA2dp.SINK_STATE_CHANGED_ACTION); + intent.putExtra(BluetoothIntent.ADDRESS, address); + intent.putExtra(BluetoothA2dp.SINK_PREVIOUS_STATE, prevState); + intent.putExtra(BluetoothA2dp.SINK_STATE, state); + mContext.sendBroadcast(intent, BLUETOOTH_PERM); - private synchronized final String lookupPath(String address) { - if (mAudioDevices == null) return null; + if (DBG) log("A2DP state : address: " + address + " State:" + prevState + "->" + state); - for (String path : mAudioDevices.keySet()) { - if (address.equals(mAudioDevices.get(path).address)) { - return path; + if (state == BluetoothA2dp.STATE_CONNECTED) { + mAudioManager.setParameter(A2DP_SINK_ADDRESS, address); + mAudioManager.setBluetoothA2dpOn(true); } } - return null; } private synchronized List<String> lookupSinksMatchingStates(int[] states) { List<String> sinks = new ArrayList<String>(); - if (mAudioDevices == null) { + if (mAudioDevices.isEmpty()) { return sinks; } - for (SinkState sink : mAudioDevices.values()) { + for (String path: mAudioDevices.keySet()) { + int sinkState = getSinkState(path); for (int state : states) { - if (sink.state == state) { - sinks.add(sink.address); + if (state == sinkState) { + sinks.add(path); break; } } @@ -441,57 +429,14 @@ public class BluetoothA2dpService extends IBluetoothA2dp.Stub { return sinks; } - private synchronized void updateState(String path, int state) { - if (mAudioDevices == null) return; - - SinkState s = mAudioDevices.get(path); - int prevState; - String address; - if (s == null) { - address = getAddressNative(path); - mAudioDevices.put(path, new SinkState(address, state)); - prevState = BluetoothA2dp.STATE_DISCONNECTED; - } else { - address = lookupAddress(path); - prevState = s.state; - s.state = state; - } - - if (state != prevState) { - if (DBG) log("state " + address + " (" + path + ") " + prevState + "->" + state); - - // keep track of the number of active sinks - if (prevState == BluetoothA2dp.STATE_DISCONNECTED) { - mSinkCount++; - } else if (state == BluetoothA2dp.STATE_DISCONNECTED) { - mSinkCount--; - } - - Intent intent = new Intent(BluetoothA2dp.SINK_STATE_CHANGED_ACTION); - intent.putExtra(BluetoothIntent.ADDRESS, address); - intent.putExtra(BluetoothA2dp.SINK_PREVIOUS_STATE, prevState); - intent.putExtra(BluetoothA2dp.SINK_STATE, state); - mContext.sendBroadcast(intent, BLUETOOTH_PERM); - - if ((prevState == BluetoothA2dp.STATE_CONNECTED || - prevState == BluetoothA2dp.STATE_PLAYING) && - (state != BluetoothA2dp.STATE_CONNECTING && - state != BluetoothA2dp.STATE_CONNECTED && - state != BluetoothA2dp.STATE_PLAYING)) { - // disconnected - intent = new Intent(AudioManager.ACTION_AUDIO_BECOMING_NOISY); - mContext.sendBroadcast(intent); - } - } - } @Override protected synchronized void dump(FileDescriptor fd, PrintWriter pw, String[] args) { - if (mAudioDevices == null) return; + if (mAudioDevices.isEmpty()) return; pw.println("Cached audio devices:"); - for (String path : mAudioDevices.keySet()) { - SinkState sink = mAudioDevices.get(path); - pw.println(path + " " + sink.address + " " + BluetoothA2dp.stateToString(sink.state)); + for (String address : mAudioDevices.keySet()) { + int state = mAudioDevices.get(address); + pw.println(address + " " + BluetoothA2dp.stateToString(state)); } } @@ -501,11 +446,7 @@ public class BluetoothA2dpService extends IBluetoothA2dp.Stub { private native boolean initNative(); private native void cleanupNative(); - private synchronized native String[] listHeadsetsNative(); - private synchronized native String createHeadsetNative(String address); - private synchronized native boolean removeHeadsetNative(String path); - private synchronized native String getAddressNative(String path); private synchronized native boolean connectSinkNative(String path); private synchronized native boolean disconnectSinkNative(String path); - private synchronized native boolean isSinkConnectedNative(String path); + private synchronized native Object []getSinkPropertiesNative(String path); } diff --git a/core/java/android/server/BluetoothDeviceService.java b/core/java/android/server/BluetoothDeviceService.java index 3a89abd..36c432b 100644 --- a/core/java/android/server/BluetoothDeviceService.java +++ b/core/java/android/server/BluetoothDeviceService.java @@ -30,7 +30,6 @@ import android.bluetooth.BluetoothError; import android.bluetooth.BluetoothHeadset; import android.bluetooth.BluetoothIntent; import android.bluetooth.IBluetoothDevice; -import android.bluetooth.IBluetoothDeviceCallback; import android.content.BroadcastReceiver; import android.content.ContentResolver; import android.content.Context; @@ -38,7 +37,6 @@ import android.content.Intent; import android.content.IntentFilter; import android.os.Binder; import android.os.Handler; -import android.os.IBinder; import android.os.Message; import android.os.RemoteException; import android.os.ServiceManager; @@ -46,6 +44,8 @@ import android.os.SystemService; import android.provider.Settings; import android.util.Log; +import com.android.internal.app.IBatteryStats; + import java.io.FileDescriptor; import java.io.PrintWriter; import java.io.UnsupportedEncodingException; @@ -55,8 +55,6 @@ import java.util.HashMap; import java.util.Iterator; import java.util.Map; -import com.android.internal.app.IBatteryStats; - public class BluetoothDeviceService extends IBluetoothDevice.Stub { private static final String TAG = "BluetoothDeviceService"; private static final boolean DBG = true; @@ -80,10 +78,12 @@ public class BluetoothDeviceService extends IBluetoothDevice.Stub { private static final int MESSAGE_REGISTER_SDP_RECORDS = 1; private static final int MESSAGE_FINISH_DISABLE = 2; + private Map<String, String> mProperties; + private HashMap <String, Map<String, String>> mRemoteDeviceProperties; + static { classInitNative(); } - private native static void classInitNative(); public BluetoothDeviceService(Context context) { mContext = context; @@ -109,8 +109,9 @@ public class BluetoothDeviceService extends IBluetoothDevice.Stub { mIsDiscovering = false; mEventLoop = new BluetoothEventLoop(mContext, this); registerForAirplaneMode(); + mProperties = new HashMap<String, String>(); + mRemoteDeviceProperties = new HashMap<String, Map<String,String>>(); } - private native void initializeNativeDataNative(); @Override protected void finalize() throws Throwable { @@ -123,13 +124,11 @@ public class BluetoothDeviceService extends IBluetoothDevice.Stub { super.finalize(); } } - private native void cleanupNativeDataNative(); public boolean isEnabled() { mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); return mBluetoothState == BluetoothDevice.BLUETOOTH_STATE_ON; } - private native int isEnabledNative(); public int getBluetoothState() { mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); @@ -150,8 +149,7 @@ public class BluetoothDeviceService extends IBluetoothDevice.Stub { * @param saveSetting If true, disable BT in settings */ public synchronized boolean disable(boolean saveSetting) { - mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, - "Need BLUETOOTH_ADMIN permission"); + mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); switch (mBluetoothState) { case BluetoothDevice.BLUETOOTH_STATE_OFF: @@ -180,6 +178,7 @@ public class BluetoothDeviceService extends IBluetoothDevice.Stub { return; } mEventLoop.stop(); + tearDownNativeDataNative(); disableNative(); // mark in progress bondings as cancelled @@ -188,25 +187,13 @@ public class BluetoothDeviceService extends IBluetoothDevice.Stub { BluetoothDevice.UNBOND_REASON_AUTH_CANCELED); } - // Remove remoteServiceChannelCallbacks - HashMap<String, IBluetoothDeviceCallback> callbacksMap = - mEventLoop.getRemoteServiceChannelCallbacks(); - - for (Iterator<String> i = callbacksMap.keySet().iterator(); i.hasNext();) { - String address = i.next(); - IBluetoothDeviceCallback callback = callbacksMap.get(address); - try { - callback.onGetRemoteServiceChannelResult(address, BluetoothError.ERROR_DISABLED); - } catch (RemoteException e) {} - i.remove(); - } - // update mode Intent intent = new Intent(BluetoothIntent.SCAN_MODE_CHANGED_ACTION); intent.putExtra(BluetoothIntent.SCAN_MODE, BluetoothDevice.SCAN_MODE_NONE); mContext.sendBroadcast(intent, BLUETOOTH_PERM); mIsDiscovering = false; + mProperties.clear(); if (saveSetting) { persistBluetoothOnSetting(false); @@ -270,7 +257,7 @@ public class BluetoothDeviceService extends IBluetoothDevice.Stub { if (!disable(false)) { mRestart = false; } - } + } private synchronized void setBluetoothState(int state) { if (state == mBluetoothState) { @@ -343,6 +330,9 @@ public class BluetoothDeviceService extends IBluetoothDevice.Stub { if (res) { + if (!setupNativeDataNative()) { + return; + } if (mSaveSetting) { persistBluetoothOnSetting(true); } @@ -369,7 +359,8 @@ public class BluetoothDeviceService extends IBluetoothDevice.Stub { if (res) { // Update mode - mEventLoop.onModeChanged(getModeNative()); + String[] propVal = {"Pairable", getProperty("Pairable")}; + mEventLoop.onPropertyChanged(propVal); } if (mIsAirplaneSensitive && isAirplaneModeOn()) { @@ -386,9 +377,6 @@ public class BluetoothDeviceService extends IBluetoothDevice.Stub { Binder.restoreCallingIdentity(origCallerIdentityToken); } - private native int enableNative(); - private native int disableNative(); - /* package */ BondState getBondState() { return mBondState; } @@ -423,14 +411,19 @@ public class BluetoothDeviceService extends IBluetoothDevice.Stub { if (mBluetoothState != BluetoothDevice.BLUETOOTH_STATE_TURNING_ON) { return; } - String[] bonds = listBondingsNative(); + String []bonds = null; + String val = getProperty("Devices"); + if (val != null) { + bonds = val.split(","); + } if (bonds == null) { return; } mState.clear(); if (DBG) log("found " + bonds.length + " bonded devices"); - for (String address : bonds) { - mState.put(address.toUpperCase(), BluetoothDevice.BOND_BONDED); + for (String device : bonds) { + mState.put(getAddressFromObjectPath(device).toUpperCase(), + BluetoothDevice.BOND_BONDED); } } @@ -528,7 +521,6 @@ public class BluetoothDeviceService extends IBluetoothDevice.Stub { } } - private native String[] listBondingsNative(); private static String toBondStateString(int bondState) { switch (bondState) { @@ -543,17 +535,36 @@ public class BluetoothDeviceService extends IBluetoothDevice.Stub { } } - public synchronized String getAddress() { + /*package*/synchronized void getAllProperties() { mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); - return getAddressNative(); + mProperties.clear(); + + String properties[] = (String [])getAdapterPropertiesNative(); + // The String Array consists of key-value pairs. + if (properties == null) { + Log.e(TAG, "*Error*: GetAdapterProperties returned NULL"); + return; + } + for (int i = 0; i < properties.length; i+=2) { + String value = null; + if (mProperties.containsKey(properties[i])) { + value = mProperties.get(properties[i]); + value = value + ',' + properties[i+1]; + } else + value = properties[i+1]; + + mProperties.put(properties[i], value); + } + + // Add adapter object path property. + String adapterPath = getAdapterPathNative(); + if (adapterPath != null) + mProperties.put("ObjectPath", adapterPath + "/dev_"); } - private native String getAddressNative(); - public synchronized String getName() { - mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); - return getNameNative(); + /* package */ synchronized void setProperty(String name, String value) { + mProperties.put(name, value); } - private native String getNameNative(); public synchronized boolean setName(String name) { mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, @@ -561,90 +572,101 @@ public class BluetoothDeviceService extends IBluetoothDevice.Stub { if (name == null) { return false; } - // hcid handles persistance of the bluetooth name - return setNameNative(name); + return setPropertyString("Name", name); } - private native boolean setNameNative(String name); - /** - * Returns the user-friendly name of a remote device. This value is - * retrned from our local cache, which is updated during device discovery. - * Do not expect to retrieve the updated remote name immediately after - * changing the name on the remote device. - * - * @param address Bluetooth address of remote device. - * - * @return The user-friendly name of the specified remote device. - */ - public synchronized String getRemoteName(String address) { + //TODO(): setPropertyString, setPropertyInteger, setPropertyBoolean + // Either have a single property function with Object as the parameter + // or have a function for each property and then obfuscate in the JNI layer. + // The following looks dirty. + private boolean setPropertyString(String key, String value) { mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); - if (!BluetoothDevice.checkBluetoothAddress(address)) { - return null; - } - return getRemoteNameNative(address); + return setAdapterPropertyStringNative(key, value); } - private native String getRemoteNameNative(String address); - /* pacakge */ native String getAdapterPathNative(); + private boolean setPropertyInteger(String key, int value) { + mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); + return setAdapterPropertyIntegerNative(key, value); + } - public synchronized boolean startDiscovery(boolean resolveNames) { - mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, - "Need BLUETOOTH_ADMIN permission"); - return startDiscoveryNative(resolveNames); + private boolean setPropertyBoolean(String key, boolean value) { + mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); + return setAdapterPropertyBooleanNative(key, value ? 1 : 0); } - private native boolean startDiscoveryNative(boolean resolveNames); - public synchronized boolean cancelDiscovery() { + /** + * Set the discoverability window for the device. A timeout of zero + * makes the device permanently discoverable (if the device is + * discoverable). Setting the timeout to a nonzero value does not make + * a device discoverable; you need to call setMode() to make the device + * explicitly discoverable. + * + * @param timeout_s The discoverable timeout in seconds. + */ + public synchronized boolean setDiscoverableTimeout(int timeout) { mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, "Need BLUETOOTH_ADMIN permission"); - return cancelDiscoveryNative(); + return setPropertyInteger("DiscoverableTimeout", timeout); } - private native boolean cancelDiscoveryNative(); - public synchronized boolean isDiscovering() { - mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); - return mIsDiscovering; - } + public synchronized boolean setScanMode(int mode) { + mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, + "Need BLUETOOTH_ADMIN permission"); + boolean pairable = false, discoverable = false; + String modeString = scanModeToBluezString(mode); + if (modeString.equals("off")) { + pairable = false; + discoverable = false; + } else if (modeString.equals("pariable")) { + pairable = true; + discoverable = false; + } else if (modeString.equals("discoverable")) { + pairable = true; + discoverable = true; + } + setPropertyBoolean("Pairable", pairable); + setPropertyBoolean("Discoverable", discoverable); - /* package */ void setIsDiscovering(boolean isDiscovering) { - mIsDiscovering = isDiscovering; + return true; } - public synchronized boolean startPeriodicDiscovery() { - mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, - "Need BLUETOOTH_ADMIN permission"); - return startPeriodicDiscoveryNative(); + /*package*/ synchronized String getProperty (String name) { + if (!mProperties.isEmpty()) + return mProperties.get(name); + getAllProperties(); + return mProperties.get(name); } - private native boolean startPeriodicDiscoveryNative(); - public synchronized boolean stopPeriodicDiscovery() { - mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, - "Need BLUETOOTH_ADMIN permission"); - return stopPeriodicDiscoveryNative(); + public synchronized String getAddress() { + mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); + return getProperty("Address"); } - private native boolean stopPeriodicDiscoveryNative(); - public synchronized boolean isPeriodicDiscovery() { + public synchronized String getName() { mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); - return isPeriodicDiscoveryNative(); + return getProperty("Name"); } - private native boolean isPeriodicDiscoveryNative(); /** - * Set the discoverability window for the device. A timeout of zero - * makes the device permanently discoverable (if the device is - * discoverable). Setting the timeout to a nonzero value does not make - * a device discoverable; you need to call setMode() to make the device - * explicitly discoverable. + * Returns the user-friendly name of a remote device. This value is + * returned from our local cache, which is updated when onPropertyChange + * event is received. + * Do not expect to retrieve the updated remote name immediately after + * changing the name on the remote device. * - * @param timeout_s The discoverable timeout in seconds. + * @param address Bluetooth address of remote device. + * + * @return The user-friendly name of the specified remote device. */ - public synchronized boolean setDiscoverableTimeout(int timeout) { - mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, - "Need BLUETOOTH_ADMIN permission"); - return setDiscoverableTimeoutNative(timeout); + public synchronized String getRemoteName(String address) { + mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); + if (!BluetoothDevice.checkBluetoothAddress(address)) { + return null; + } + Map <String, String> properties = mRemoteDeviceProperties.get(address); + if (properties != null) return properties.get("Name"); + return null; } - private native boolean setDiscoverableTimeoutNative(int timeout_s); /** * Get the discoverability window for the device. A timeout of zero @@ -656,45 +678,46 @@ public class BluetoothDeviceService extends IBluetoothDevice.Stub { */ public synchronized int getDiscoverableTimeout() { mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); - return getDiscoverableTimeoutNative(); - } - private native int getDiscoverableTimeoutNative(); - - public synchronized boolean isAclConnected(String address) { - mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); - if (!BluetoothDevice.checkBluetoothAddress(address)) { - return false; - } - return isConnectedNative(address); + String timeout = getProperty("DiscoverableTimeout"); + if (timeout != null) + return Integer.valueOf(timeout); + else + return -1; } - private native boolean isConnectedNative(String address); public synchronized int getScanMode() { mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); - return bluezStringToScanMode(getModeNative()); + if (!isEnabled()) + return BluetoothError.ERROR; + + boolean pairable = getProperty("Pairable").equals("true"); + boolean discoverable = getProperty("Discoverable").equals("true"); + return bluezStringToScanMode (pairable, discoverable); } - private native String getModeNative(); - public synchronized boolean setScanMode(int mode) { + public synchronized boolean startDiscovery() { mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, "Need BLUETOOTH_ADMIN permission"); - String bluezMode = scanModeToBluezString(mode); - if (bluezMode != null) { - return setModeNative(bluezMode); + if (!isEnabled()) { + return false; } - return false; + return startDiscoveryNative(); } - private native boolean setModeNative(String mode); - public synchronized boolean disconnectRemoteDeviceAcl(String address) { + public synchronized boolean cancelDiscovery() { mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, "Need BLUETOOTH_ADMIN permission"); - if (!BluetoothDevice.checkBluetoothAddress(address)) { - return false; - } - return disconnectRemoteDeviceNative(address); + return stopDiscoveryNative(); + } + + public synchronized boolean isDiscovering() { + mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); + return mIsDiscovering; + } + + /* package */ void setIsDiscovering(boolean isDiscovering) { + mIsDiscovering = isDiscovering; } - private native boolean disconnectRemoteDeviceNative(String address); public synchronized boolean createBond(String address) { mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, @@ -719,14 +742,13 @@ public class BluetoothDeviceService extends IBluetoothDevice.Stub { return false; } - if (!createBondingNative(address, 60000 /* 1 minute */)) { + if (!createPairedDeviceNative(address, 60000 /* 1 minute */)) { return false; } mBondState.setBondState(address, BluetoothDevice.BOND_BONDING); return true; } - private native boolean createBondingNative(String address, int timeout_ms); public synchronized boolean cancelBondProcess(String address) { mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, @@ -741,10 +763,9 @@ public class BluetoothDeviceService extends IBluetoothDevice.Stub { mBondState.setBondState(address, BluetoothDevice.BOND_NOT_BONDED, BluetoothDevice.UNBOND_REASON_AUTH_CANCELED); - cancelBondingProcessNative(address); + cancelDeviceCreationNative(address); return true; } - private native boolean cancelBondingProcessNative(String address); public synchronized boolean removeBond(String address) { mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, @@ -752,9 +773,8 @@ public class BluetoothDeviceService extends IBluetoothDevice.Stub { if (!BluetoothDevice.checkBluetoothAddress(address)) { return false; } - return removeBondingNative(address); + return removeDeviceNative(getObjectPathFromAddress(address)); } - private native boolean removeBondingNative(String address); public synchronized String[] listBonds() { mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); @@ -769,198 +789,57 @@ public class BluetoothDeviceService extends IBluetoothDevice.Stub { return mBondState.getBondState(address.toUpperCase()); } - public synchronized String[] listAclConnections() { - mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); - return listConnectionsNative(); - } - private native String[] listConnectionsNative(); - - /** - * This method lists all remote devices that this adapter is aware of. - * This is a list not only of all most-recently discovered devices, but of - * all devices discovered by this adapter up to some point in the past. - * Note that many of these devices may not be in the neighborhood anymore, - * and attempting to connect to them will result in an error. - * - * @return An array of strings representing the Bluetooth addresses of all - * remote devices that this adapter is aware of. - */ - public synchronized String[] listRemoteDevices() { - mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); - return listRemoteDevicesNative(); - } - private native String[] listRemoteDevicesNative(); - - /** - * Returns the version of the Bluetooth chip. This version is compiled from - * the LMP version. In case of EDR the features attribute must be checked. - * Example: "Bluetooth 2.0 + EDR". - * - * @return a String representation of the this Adapter's underlying - * Bluetooth-chip version. - */ - public synchronized String getVersion() { - mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); - return getVersionNative(); - } - private native String getVersionNative(); - - /** - * Returns the revision of the Bluetooth chip. This is a vendor-specific - * value and in most cases it represents the firmware version. This might - * derive from the HCI revision and LMP subversion values or via extra - * vendord specific commands. - * In case the revision of a chip is not available. This method should - * return the LMP subversion value as a string. - * Example: "HCI 19.2" - * - * @return The HCI revision of this adapter. - */ - public synchronized String getRevision() { - mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); - return getRevisionNative(); - } - private native String getRevisionNative(); - - /** - * Returns the manufacturer of the Bluetooth chip. If the company id is not - * known the sting "Company ID %d" where %d should be replaced with the - * numeric value from the manufacturer field. - * Example: "Cambridge Silicon Radio" - * - * @return Manufacturer name. - */ - public synchronized String getManufacturer() { - mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); - return getManufacturerNative(); - } - private native String getManufacturerNative(); - - /** - * Returns the company name from the OUI database of the Bluetooth device - * address. This function will need a valid and up-to-date oui.txt from - * the IEEE. This value will be different from the manufacturer string in - * the most cases. - * If the oui.txt file is not present or the OUI part of the Bluetooth - * address is not listed, it should return the string "OUI %s" where %s is - * the actual OUI. - * - * Example: "Apple Computer" - * - * @return company name - */ - public synchronized String getCompany() { - mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); - return getCompanyNative(); - } - private native String getCompanyNative(); - - /** - * Like getVersion(), but for a remote device. - * - * @param address The Bluetooth address of the remote device. - * - * @return remote-device Bluetooth version - * - * @see #getVersion - */ - public synchronized String getRemoteVersion(String address) { - mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); - if (!BluetoothDevice.checkBluetoothAddress(address)) { - return null; - } - return getRemoteVersionNative(address); - } - private native String getRemoteVersionNative(String address); - - /** - * Like getRevision(), but for a remote device. - * - * @param address The Bluetooth address of the remote device. - * - * @return remote-device HCI revision - * - * @see #getRevision - */ - public synchronized String getRemoteRevision(String address) { - mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); - if (!BluetoothDevice.checkBluetoothAddress(address)) { - return null; - } - return getRemoteRevisionNative(address); - } - private native String getRemoteRevisionNative(String address); - - /** - * Like getManufacturer(), but for a remote device. - * - * @param address The Bluetooth address of the remote device. - * - * @return remote-device Bluetooth chip manufacturer - * - * @see #getManufacturer - */ - public synchronized String getRemoteManufacturer(String address) { - mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); - if (!BluetoothDevice.checkBluetoothAddress(address)) { - return null; + /*package*/ synchronized String getRemoteDeviceProperty(String address, String property) { + Map<String, String> properties = mRemoteDeviceProperties.get(address); + if (properties != null) { + return properties.get(property); + } else { + // Query for remote device properties, again. + // We will need to reload the cache when we switch Bluetooth on / off + // or if we crash. + String objectPath = getObjectPathFromAddress(address); + String propValues[] = (String [])getDevicePropertiesNative(objectPath); + if (propValues != null) { + addRemoteDeviceProperties(address, propValues); + return getRemoteDeviceProperty(address, property); + } } - return getRemoteManufacturerNative(address); + Log.e(TAG, "getRemoteDeviceProperty: " + property + "not present:" + address); + return null; } - private native String getRemoteManufacturerNative(String address); - /** - * Like getCompany(), but for a remote device. - * - * @param address The Bluetooth address of the remote device. - * - * @return remote-device company - * - * @see #getCompany - */ - public synchronized String getRemoteCompany(String address) { - mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); - if (!BluetoothDevice.checkBluetoothAddress(address)) { - return null; + /* package */ synchronized void addRemoteDeviceProperties(String address, String[] properties) { + Map<String, String> propertyValues = new HashMap<String, String>(); + for (int i = 0; i < properties.length; i+=2) { + String value = null; + if (propertyValues.containsKey(properties[i])) { + value = propertyValues.get(properties[i]); + value = value + ',' + properties[i+1]; + } else { + value = properties[i+1]; + } + propertyValues.put(properties[i], value); } - return getRemoteCompanyNative(address); + mRemoteDeviceProperties.put(address, propertyValues); } - private native String getRemoteCompanyNative(String address); - /** - * Returns the date and time when the specified remote device has been seen - * by a discover procedure. - * Example: "2006-02-08 12:00:00 GMT" - * - * @return a String with the timestamp. - */ - public synchronized String lastSeen(String address) { - mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); - if (!BluetoothDevice.checkBluetoothAddress(address)) { - return null; - } - return lastSeenNative(address); + /* package */ void removeRemoteDeviceProperties(String address) { + mRemoteDeviceProperties.remove(address); } - private native String lastSeenNative(String address); - /** - * Returns the date and time when the specified remote device has last been - * connected to - * Example: "2006-02-08 12:00:00 GMT" - * - * @return a String with the timestamp. - */ - public synchronized String lastUsed(String address) { - mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); - if (!BluetoothDevice.checkBluetoothAddress(address)) { - return null; + /* package */ synchronized void setRemoteDeviceProperty(String address, String name, + String value) { + Map <String, String> propVal = mRemoteDeviceProperties.get(address); + if (propVal != null) { + propVal.put(name, value); + mRemoteDeviceProperties.put(address, propVal); + } else { + Log.e(TAG, "setRemoteDeviceProperty for a device not in cache:" + address); } - return lastUsedNative(address); } - private native String lastUsedNative(String address); /** - * Gets the remote major, minor, and service classes encoded as a 32-bit + * Gets the remote major, minor classes encoded as a 32-bit * integer. * * Note: this value is retrieved from cache, because we get it during @@ -968,120 +847,56 @@ public class BluetoothDeviceService extends IBluetoothDevice.Stub { * * @return 32-bit integer encoding the remote major, minor, and service * classes. - * - * @see #getRemoteMajorClass - * @see #getRemoteMinorClass - * @see #getRemoteServiceClasses */ public synchronized int getRemoteClass(String address) { if (!BluetoothDevice.checkBluetoothAddress(address)) { mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); return BluetoothClass.ERROR; } - return getRemoteClassNative(address); + String val = getRemoteDeviceProperty(address, "Class"); + if (val == null) + return BluetoothClass.ERROR; + else { + return Integer.valueOf(val); + } } - private native int getRemoteClassNative(String address); + /** * Gets the remote features encoded as bit mask. * * Note: This method may be obsoleted soon. * - * @return byte array of features. - */ - public synchronized byte[] getRemoteFeatures(String address) { - mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); - if (!BluetoothDevice.checkBluetoothAddress(address)) { - return null; - } - return getRemoteFeaturesNative(address); - } - private native byte[] getRemoteFeaturesNative(String address); - - /** - * This method and {@link #getRemoteServiceRecord} query the SDP service - * on a remote device. They do not interpret the data, but simply return - * it raw to the user. To read more about SDP service handles and records, - * consult the Bluetooth core documentation (www.bluetooth.com). - * - * @param address Bluetooth address of remote device. - * @param match a String match to narrow down the service-handle search. - * The only supported value currently is "hsp" for the headset - * profile. To retrieve all service handles, simply pass an empty - * match string. - * - * @return all service handles corresponding to the string match. - * - * @see #getRemoteServiceRecord + * @return String array of 128bit UUIDs */ - public synchronized int[] getRemoteServiceHandles(String address, String match) { + public synchronized String[] getRemoteUuids(String address) { mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); if (!BluetoothDevice.checkBluetoothAddress(address)) { return null; } - if (match == null) { - match = ""; - } - return getRemoteServiceHandlesNative(address, match); + String value = getRemoteDeviceProperty(address, "UUIDs"); + String[] uuids = null; + // The UUIDs are stored as a "," separated string. + if (value != null) + uuids = value.split(","); + return uuids; } - private native int[] getRemoteServiceHandlesNative(String address, String match); /** - * This method retrieves the service records corresponding to a given - * service handle (method {@link #getRemoteServiceHandles} retrieves the - * service handles.) + * Gets the rfcomm channel associated with the UUID. * - * This method and {@link #getRemoteServiceHandles} do not interpret their - * data, but simply return it raw to the user. To read more about SDP - * service handles and records, consult the Bluetooth core documentation - * (www.bluetooth.com). + * @param address Address of the remote device + * @param uuid UUID of the service attribute * - * @param address Bluetooth address of remote device. - * @param handle Service handle returned by {@link #getRemoteServiceHandles} - * - * @return a byte array of all service records corresponding to the - * specified service handle. - * - * @see #getRemoteServiceHandles + * @return rfcomm channel associated with the service attribute */ - public synchronized byte[] getRemoteServiceRecord(String address, int handle) { - mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); - if (!BluetoothDevice.checkBluetoothAddress(address)) { - return null; - } - return getRemoteServiceRecordNative(address, handle); - } - private native byte[] getRemoteServiceRecordNative(String address, int handle); - - private static final int MAX_OUTSTANDING_ASYNC = 32; - - // AIDL does not yet support short's - public synchronized boolean getRemoteServiceChannel(String address, int uuid16, - IBluetoothDeviceCallback callback) { + public int getRemoteServiceChannel(String address, String uuid) { mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); if (!BluetoothDevice.checkBluetoothAddress(address)) { - return false; - } - HashMap<String, IBluetoothDeviceCallback> callbacks = - mEventLoop.getRemoteServiceChannelCallbacks(); - if (callbacks.containsKey(address)) { - Log.w(TAG, "SDP request already in progress for " + address); - return false; + return BluetoothError.ERROR_IPC; } - // Protect from malicious clients - only allow 32 bonding requests per minute. - if (callbacks.size() > MAX_OUTSTANDING_ASYNC) { - Log.w(TAG, "Too many outstanding SDP requests, dropping request for " + address); - return false; - } - callbacks.put(address, callback); - - if (!getRemoteServiceChannelNative(address, (short)uuid16)) { - callbacks.remove(address); - return false; - } - return true; + return getDeviceServiceChannelNative(getObjectPathFromAddress(address), uuid, 0x0004); } - private native boolean getRemoteServiceChannelNative(String address, short uuid16); public synchronized boolean setPin(String address, byte[] pin) { mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, @@ -1108,7 +923,6 @@ public class BluetoothDeviceService extends IBluetoothDevice.Stub { } return setPinNative(address, pinString, data.intValue()); } - private native boolean setPinNative(String address, String pin, int nativeData); public synchronized boolean cancelPin(String address) { mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, @@ -1126,7 +940,6 @@ public class BluetoothDeviceService extends IBluetoothDevice.Stub { } return cancelPinNative(address, data.intValue()); } - private native boolean cancelPinNative(String address, int natveiData); private final BroadcastReceiver mReceiver = new BroadcastReceiver() { @Override @@ -1190,20 +1003,22 @@ public class BluetoothDeviceService extends IBluetoothDevice.Stub { BluetoothHeadset headset = new BluetoothHeadset(mContext, null); - String[] addresses = listRemoteDevices(); - pw.println("\n--Known devices--"); - for (String address : addresses) { + for (String address : mRemoteDeviceProperties.keySet()) { pw.printf("%s %10s (%d) %s\n", address, toBondStateString(mBondState.getBondState(address)), mBondState.getAttempt(address), getRemoteName(address)); } - addresses = listAclConnections(); + String value = getProperty("Devices"); + String []devicesObjectPath = null; + if (value != null) { + devicesObjectPath = value.split(","); + } pw.println("\n--ACL connected devices--"); - for (String address : addresses) { - pw.println(address); + for (String device : devicesObjectPath) { + pw.println(getAddressFromObjectPath(device)); } // Rather not do this from here, but no-where else and I need this @@ -1227,20 +1042,13 @@ public class BluetoothDeviceService extends IBluetoothDevice.Stub { headset.close(); } - /* package */ static int bluezStringToScanMode(String mode) { - if (mode == null) { - return BluetoothError.ERROR; - } - mode = mode.toLowerCase(); - if (mode.equals("off")) { - return BluetoothDevice.SCAN_MODE_NONE; - } else if (mode.equals("connectable")) { - return BluetoothDevice.SCAN_MODE_CONNECTABLE; - } else if (mode.equals("discoverable")) { + /* package */ static int bluezStringToScanMode(boolean pairable, boolean discoverable) { + if (pairable && discoverable) return BluetoothDevice.SCAN_MODE_CONNECTABLE_DISCOVERABLE; - } else { - return BluetoothError.ERROR; - } + else if (pairable && !discoverable) + return BluetoothDevice.SCAN_MODE_CONNECTABLE; + else + return BluetoothDevice.SCAN_MODE_NONE; } /* package */ static String scanModeToBluezString(int mode) { @@ -1255,7 +1063,67 @@ public class BluetoothDeviceService extends IBluetoothDevice.Stub { return null; } + /*package*/ String getAddressFromObjectPath(String objectPath) { + String adapterObjectPath = getProperty("ObjectPath"); + if (adapterObjectPath == null || objectPath == null) { + Log.e(TAG, "getAddressFromObjectPath: AdpaterObjectPath:" + adapterObjectPath + + " or deviceObjectPath:" + objectPath + " is null"); + return null; + } + if (!objectPath.startsWith(adapterObjectPath)) { + Log.e(TAG, "getAddressFromObjectPath: AdpaterObjectPath:" + adapterObjectPath + + " is not a prefix of deviceObjectPath:" + objectPath + + "bluetoothd crashed ?"); + return null; + } + String address = objectPath.substring(adapterObjectPath.length()); + if (address != null) return address.replace('_', ':'); + + Log.e(TAG, "getAddressFromObjectPath: Address being returned is null"); + return null; + } + + /*package*/ String getObjectPathFromAddress(String address) { + String path = getProperty("ObjectPath"); + if (path == null) { + Log.e(TAG, "Error: Object Path is null"); + return null; + } + path = path + address.replace(":", "_"); + return path; + } + private static void log(String msg) { Log.d(TAG, msg); } + + private native static void classInitNative(); + private native void initializeNativeDataNative(); + private native boolean setupNativeDataNative(); + private native boolean tearDownNativeDataNative(); + private native void cleanupNativeDataNative(); + private native String getAdapterPathNative(); + + private native int isEnabledNative(); + private native int enableNative(); + private native int disableNative(); + + private native Object[] getAdapterPropertiesNative(); + private native Object[] getDevicePropertiesNative(String objectPath); + private native boolean setAdapterPropertyStringNative(String key, String value); + private native boolean setAdapterPropertyIntegerNative(String key, int value); + private native boolean setAdapterPropertyBooleanNative(String key, int value); + + private native boolean startDiscoveryNative(); + private native boolean stopDiscoveryNative(); + + private native boolean createPairedDeviceNative(String address, int timeout_ms); + private native boolean cancelDeviceCreationNative(String address); + private native boolean removeDeviceNative(String objectPath); + private native int getDeviceServiceChannelNative(String objectPath, String uuid, + int attributeId); + + private native boolean cancelPinNative(String address, int nativeData); + private native boolean setPinNative(String address, String pin, int nativeData); + } diff --git a/core/java/android/server/BluetoothEventLoop.java b/core/java/android/server/BluetoothEventLoop.java index 8cc229b..00c13b7 100644 --- a/core/java/android/server/BluetoothEventLoop.java +++ b/core/java/android/server/BluetoothEventLoop.java @@ -21,15 +21,15 @@ import android.bluetooth.BluetoothClass; import android.bluetooth.BluetoothDevice; import android.bluetooth.BluetoothError; import android.bluetooth.BluetoothIntent; -import android.bluetooth.IBluetoothDeviceCallback; +import android.bluetooth.BluetoothUuid; import android.content.Context; import android.content.Intent; import android.os.Handler; import android.os.Message; -import android.os.RemoteException; import android.util.Log; import java.util.HashMap; +import java.util.UUID; /** * TODO: Move this to @@ -47,7 +47,6 @@ class BluetoothEventLoop { private boolean mStarted; private boolean mInterrupted; private final HashMap<String, Integer> mPasskeyAgentRequestData; - private final HashMap<String, IBluetoothDeviceCallback> mGetRemoteServiceChannelCallbacks; private final BluetoothDeviceService mBluetoothService; private final Context mContext; @@ -89,10 +88,8 @@ class BluetoothEventLoop { mBluetoothService = bluetoothService; mContext = context; mPasskeyAgentRequestData = new HashMap(); - mGetRemoteServiceChannelCallbacks = new HashMap(); initializeNativeDataNative(); } - private native void initializeNativeDataNative(); protected void finalize() throws Throwable { try { @@ -101,20 +98,11 @@ class BluetoothEventLoop { super.finalize(); } } - private native void cleanupNativeDataNative(); - /* pacakge */ HashMap<String, IBluetoothDeviceCallback> getRemoteServiceChannelCallbacks() { - return mGetRemoteServiceChannelCallbacks; - } - - /* pacakge */ HashMap<String, Integer> getPasskeyAgentRequestData() { + /* package */ HashMap<String, Integer> getPasskeyAgentRequestData() { return mPasskeyAgentRequestData; } - private native void startEventLoopNative(); - private native void stopEventLoopNative(); - private native boolean isEventLoopRunningNative(); - /* package */ void start() { if (!isEventLoopRunningNative()) { @@ -134,79 +122,37 @@ class BluetoothEventLoop { return isEventLoopRunningNative(); } - /*package*/ void onModeChanged(String bluezMode) { - int mode = BluetoothDeviceService.bluezStringToScanMode(bluezMode); - if (mode >= 0) { - Intent intent = new Intent(BluetoothIntent.SCAN_MODE_CHANGED_ACTION); - intent.putExtra(BluetoothIntent.SCAN_MODE, mode); - intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT); + private void onDeviceFound(String address, String[] properties) { + if (properties == null) { + Log.e(TAG, "ERROR: Remote device properties are null"); + return; + } + mBluetoothService.addRemoteDeviceProperties(address, properties); + String rssi = mBluetoothService.getRemoteDeviceProperty(address, "RSSI"); + String classValue = mBluetoothService.getRemoteDeviceProperty(address, "Class"); + + if (rssi != null && classValue != null) { + Intent intent = new Intent(BluetoothIntent.REMOTE_DEVICE_FOUND_ACTION); + intent.putExtra(BluetoothIntent.ADDRESS, address); + intent.putExtra(BluetoothIntent.CLASS, classValue); + intent.putExtra(BluetoothIntent.RSSI, (short)Integer.valueOf(rssi).intValue()); + mContext.sendBroadcast(intent, BLUETOOTH_PERM); + } else { + log ("RSSI: " + rssi + " or ClassValue: " + classValue + + " for remote device: " + address + " is null"); } } - private void onDiscoveryStarted() { - mBluetoothService.setIsDiscovering(true); - Intent intent = new Intent(BluetoothIntent.DISCOVERY_STARTED_ACTION); - mContext.sendBroadcast(intent, BLUETOOTH_PERM); - } - private void onDiscoveryCompleted() { - mBluetoothService.setIsDiscovering(false); - Intent intent = new Intent(BluetoothIntent.DISCOVERY_COMPLETED_ACTION); - mContext.sendBroadcast(intent, BLUETOOTH_PERM); - } + private void onDeviceDisappeared(String address) { + mBluetoothService.removeRemoteDeviceProperties(address); - private void onRemoteDeviceFound(String address, int deviceClass, short rssi) { - Intent intent = new Intent(BluetoothIntent.REMOTE_DEVICE_FOUND_ACTION); - intent.putExtra(BluetoothIntent.ADDRESS, address); - intent.putExtra(BluetoothIntent.CLASS, deviceClass); - intent.putExtra(BluetoothIntent.RSSI, rssi); - mContext.sendBroadcast(intent, BLUETOOTH_PERM); - } - private void onRemoteDeviceDisappeared(String address) { Intent intent = new Intent(BluetoothIntent.REMOTE_DEVICE_DISAPPEARED_ACTION); intent.putExtra(BluetoothIntent.ADDRESS, address); mContext.sendBroadcast(intent, BLUETOOTH_PERM); } - private void onRemoteClassUpdated(String address, int deviceClass) { - Intent intent = new Intent(BluetoothIntent.REMOTE_DEVICE_CLASS_UPDATED_ACTION); - intent.putExtra(BluetoothIntent.ADDRESS, address); - intent.putExtra(BluetoothIntent.CLASS, deviceClass); - mContext.sendBroadcast(intent, BLUETOOTH_PERM); - } - private void onRemoteDeviceConnected(String address) { - Intent intent = new Intent(BluetoothIntent.REMOTE_DEVICE_CONNECTED_ACTION); - intent.putExtra(BluetoothIntent.ADDRESS, address); - mContext.sendBroadcast(intent, BLUETOOTH_PERM); - } - private void onRemoteDeviceDisconnectRequested(String address) { - Intent intent = new Intent(BluetoothIntent.REMOTE_DEVICE_DISCONNECT_REQUESTED_ACTION); - intent.putExtra(BluetoothIntent.ADDRESS, address); - mContext.sendBroadcast(intent, BLUETOOTH_PERM); - } - private void onRemoteDeviceDisconnected(String address) { - Intent intent = new Intent(BluetoothIntent.REMOTE_DEVICE_DISCONNECTED_ACTION); - intent.putExtra(BluetoothIntent.ADDRESS, address); - mContext.sendBroadcast(intent, BLUETOOTH_PERM); - } - private void onRemoteNameUpdated(String address, String name) { - Intent intent = new Intent(BluetoothIntent.REMOTE_NAME_UPDATED_ACTION); - intent.putExtra(BluetoothIntent.ADDRESS, address); - intent.putExtra(BluetoothIntent.NAME, name); - mContext.sendBroadcast(intent, BLUETOOTH_PERM); - } - private void onRemoteNameFailed(String address) { - Intent intent = new Intent(BluetoothIntent.REMOTE_NAME_FAILED_ACTION); - intent.putExtra(BluetoothIntent.ADDRESS, address); - mContext.sendBroadcast(intent, BLUETOOTH_PERM); - } - private void onRemoteNameChanged(String address, String name) { - Intent intent = new Intent(BluetoothIntent.REMOTE_NAME_UPDATED_ACTION); - intent.putExtra(BluetoothIntent.ADDRESS, address); - intent.putExtra(BluetoothIntent.NAME, name); - mContext.sendBroadcast(intent, BLUETOOTH_PERM); - } - private void onCreateBondingResult(String address, int result) { + private void onCreatePairedDeviceResult(String address, int result) { address = address.toUpperCase(); if (result == BluetoothError.SUCCESS) { mBluetoothService.getBondState().setBondState(address, BluetoothDevice.BOND_BONDED); @@ -259,23 +205,118 @@ class BluetoothEventLoop { mBluetoothService.getBondState().attempt(address); } - private void onBondingCreated(String address) { - mBluetoothService.getBondState().setBondState(address.toUpperCase(), - BluetoothDevice.BOND_BONDED); + private void onDeviceCreated(String deviceObjectPath) { + // do nothing. + return; } - private void onBondingRemoved(String address) { - mBluetoothService.getBondState().setBondState(address.toUpperCase(), - BluetoothDevice.BOND_NOT_BONDED, BluetoothDevice.UNBOND_REASON_REMOVED); + private void onDeviceRemoved(String deviceObjectPath) { + String address = mBluetoothService.getAddressFromObjectPath(deviceObjectPath); + if (address != null) + mBluetoothService.getBondState().setBondState(address.toUpperCase(), + BluetoothDevice.BOND_NOT_BONDED, BluetoothDevice.UNBOND_REASON_REMOVED); } - private void onNameChanged(String name) { - Intent intent = new Intent(BluetoothIntent.NAME_CHANGED_ACTION); - intent.putExtra(BluetoothIntent.NAME, name); - mContext.sendBroadcast(intent, BLUETOOTH_PERM); + /*package*/ void onPropertyChanged(String[] propValues) { + String name = propValues[0]; + if (name.equals("Name")) { + Intent intent = new Intent(BluetoothIntent.NAME_CHANGED_ACTION); + intent.putExtra(BluetoothIntent.NAME, propValues[1]); + mContext.sendBroadcast(intent, BLUETOOTH_PERM); + mBluetoothService.setProperty(name, propValues[1]); + } else if (name.equals("Pairable") || name.equals("Discoverable")) { + String pairable = name.equals("Pairable") ? propValues[1] : + mBluetoothService.getProperty("Pairable"); + String discoverable = name.equals("Discoverable") ? propValues[1] : + mBluetoothService.getProperty("Discoverable"); + + // This shouldn't happen, unless Adapter Properties are null. + if (pairable == null || discoverable == null) + return; + + int mode = BluetoothDeviceService.bluezStringToScanMode( + pairable.equals("true"), + discoverable.equals("true")); + if (mode >= 0) { + Intent intent = new Intent(BluetoothIntent.SCAN_MODE_CHANGED_ACTION); + intent.putExtra(BluetoothIntent.SCAN_MODE, mode); + intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT); + mContext.sendBroadcast(intent, BLUETOOTH_PERM); + } + mBluetoothService.setProperty(name, propValues[1]); + } else if (name.equals("Discovering")) { + Intent intent; + if (propValues[1].equals("true")) { + mBluetoothService.setIsDiscovering(true); + intent = new Intent(BluetoothIntent.DISCOVERY_STARTED_ACTION); + } else { + // Stop the discovery. + mBluetoothService.cancelDiscovery(); + mBluetoothService.setIsDiscovering(false); + intent = new Intent(BluetoothIntent.DISCOVERY_COMPLETED_ACTION); + } + mContext.sendBroadcast(intent, BLUETOOTH_PERM); + mBluetoothService.setProperty(name, propValues[1]); + } else if (name.equals("Devices")) { + String value = ""; + for (int i = 1; i < propValues.length; i++) { + value = value + propValues[i] + ','; + } + mBluetoothService.setProperty(name, value.equals("") ? null : value); + } else if (name.equals("Powered")) { + // bluetoothd has restarted, re-read all our properties. + // Note: bluez only sends this property change when it restarts. + if (propValues[1].equals("true")) + onRestartRequired(); + } + } + + private void onDevicePropertyChanged(String deviceObjectPath, String[] propValues) { + String name = propValues[0]; + String address = mBluetoothService.getAddressFromObjectPath(deviceObjectPath); + if (address == null) { + Log.e(TAG, "onDevicePropertyChanged: Address of the remote device in null"); + return; + } + if (name.equals("Name")) { + Intent intent = new Intent(BluetoothIntent.REMOTE_NAME_UPDATED_ACTION); + intent.putExtra(BluetoothIntent.ADDRESS, address); + intent.putExtra(BluetoothIntent.NAME, propValues[1]); + mContext.sendBroadcast(intent, BLUETOOTH_PERM); + mBluetoothService.setRemoteDeviceProperty(address, name, propValues[1]); + } else if (name.equals("Class")) { + Intent intent = new Intent(BluetoothIntent.REMOTE_DEVICE_CLASS_UPDATED_ACTION); + intent.putExtra(BluetoothIntent.ADDRESS, address); + intent.putExtra(BluetoothIntent.CLASS, propValues[1]); + mContext.sendBroadcast(intent, BLUETOOTH_PERM); + mBluetoothService.setRemoteDeviceProperty(address, name, propValues[1]); + } else if (name.equals("Connected")) { + Intent intent = null; + if (propValues[1].equals("true")) { + intent = new Intent(BluetoothIntent.REMOTE_DEVICE_CONNECTED_ACTION); + } else { + intent = new Intent(BluetoothIntent.REMOTE_DEVICE_DISCONNECTED_ACTION); + } + intent.putExtra(BluetoothIntent.ADDRESS, address); + mContext.sendBroadcast(intent, BLUETOOTH_PERM); + mBluetoothService.setRemoteDeviceProperty(address, name, propValues[1]); + } else if (name.equals("UUIDs")) { + String uuid = "" ; + for (int i = 1; i < propValues.length; i++) { + uuid = uuid + propValues[i] + ","; + } + mBluetoothService.setRemoteDeviceProperty(address, name, + uuid.equals("") ? null : uuid); + } + } - private void onPasskeyAgentRequest(String address, int nativeData) { + private void onRequestPinCode(String objectPath, int nativeData) { + String address = mBluetoothService.getAddressFromObjectPath(objectPath); + if (address == null) { + Log.e(TAG, "Unable to get device address in onRequestPinCode, returning null"); + return; + } address = address.toUpperCase(); mPasskeyAgentRequestData.put(address, new Integer(nativeData)); @@ -309,21 +350,21 @@ class BluetoothEventLoop { Intent intent = new Intent(BluetoothIntent.PAIRING_REQUEST_ACTION); intent.putExtra(BluetoothIntent.ADDRESS, address); mContext.sendBroadcast(intent, BLUETOOTH_ADMIN_PERM); + return; } - private void onPasskeyAgentCancel(String address) { - address = address.toUpperCase(); - mBluetoothService.cancelPin(address); - Intent intent = new Intent(BluetoothIntent.PAIRING_CANCEL_ACTION); - intent.putExtra(BluetoothIntent.ADDRESS, address); - mContext.sendBroadcast(intent, BLUETOOTH_ADMIN_PERM); - mBluetoothService.getBondState().setBondState(address, BluetoothDevice.BOND_NOT_BONDED, - BluetoothDevice.UNBOND_REASON_AUTH_CANCELED); - } + private boolean onAgentAuthorize(String objectPath, String deviceUuid) { + String address = mBluetoothService.getAddressFromObjectPath(objectPath); + if (address == null) { + Log.e(TAG, "Unable to get device address in onAuthAgentAuthorize"); + return false; + } - private boolean onAuthAgentAuthorize(String address, String service, String uuid) { boolean authorized = false; - if (mBluetoothService.isEnabled() && service.endsWith("service_audio")) { + UUID uuid = UUID.fromString(deviceUuid); + if (mBluetoothService.isEnabled() && (BluetoothUuid.isAudioSink(uuid) || + BluetoothUuid.isAudioSource(uuid) || + BluetoothUuid.isAdvAudioDist(uuid))) { BluetoothA2dp a2dp = new BluetoothA2dp(mContext); authorized = a2dp.getSinkPriority(address) > BluetoothA2dp.PRIORITY_OFF; if (authorized) { @@ -332,30 +373,21 @@ class BluetoothEventLoop { Log.i(TAG, "Rejecting incoming A2DP connection from " + address); } } else { - Log.i(TAG, "Rejecting incoming " + service + " connection from " + address); + Log.i(TAG, "Rejecting incoming " + deviceUuid + " connection from " + address); } return authorized; } - private void onAuthAgentCancel(String address, String service, String uuid) { + private void onAgentCancel() { // We immediately response to DBUS Authorize() so this should not // usually happen - log("onAuthAgentCancel(" + address + ", " + service + ", " + uuid + ")"); - } - - private void onGetRemoteServiceChannelResult(String address, int channel) { - IBluetoothDeviceCallback callback = mGetRemoteServiceChannelCallbacks.get(address); - if (callback != null) { - mGetRemoteServiceChannelCallbacks.remove(address); - try { - callback.onGetRemoteServiceChannelResult(address, channel); - } catch (RemoteException e) {} - } + log("onAgentCancel"); } private void onRestartRequired() { if (mBluetoothService.isEnabled()) { - Log.e(TAG, "*** A serious error occured (did hcid crash?) - restarting Bluetooth ***"); + Log.e(TAG, "*** A serious error occured (did bluetoothd crash?) - " + + "restarting Bluetooth ***"); mHandler.sendEmptyMessage(EVENT_RESTART_BLUETOOTH); } } @@ -363,4 +395,10 @@ class BluetoothEventLoop { private static void log(String msg) { Log.d(TAG, msg); } + + private native void initializeNativeDataNative(); + private native void startEventLoopNative(); + private native void stopEventLoopNative(); + private native boolean isEventLoopRunningNative(); + private native void cleanupNativeDataNative(); } |