diff options
author | Jaikumar Ganesh <jaikumar@google.com> | 2010-06-02 14:36:14 -0700 |
---|---|---|
committer | Jaikumar Ganesh <jaikumar@google.com> | 2010-06-02 16:03:45 -0700 |
commit | 9b637e5985f9a86f39d70335c0390ade3716592a (patch) | |
tree | 1ffa5bfee87452ebdb619a075fab765714e916d3 /core/java/android/server | |
parent | c87d5849f22d805b3dde8f70f60ddfadfc7c7b9d (diff) | |
download | frameworks_base-9b637e5985f9a86f39d70335c0390ade3716592a.zip frameworks_base-9b637e5985f9a86f39d70335c0390ade3716592a.tar.gz frameworks_base-9b637e5985f9a86f39d70335c0390ade3716592a.tar.bz2 |
Add a new state machine for handling the incoming / outgoing profile
connections.
Change-Id: I5fc9170b5e24c4a52a6f2ef4ca7a8bac65271941
Diffstat (limited to 'core/java/android/server')
-rw-r--r-- | core/java/android/server/BluetoothA2dpService.java | 108 | ||||
-rw-r--r-- | core/java/android/server/BluetoothEventLoop.java | 1 | ||||
-rw-r--r-- | core/java/android/server/BluetoothService.java | 124 |
3 files changed, 183 insertions, 50 deletions
diff --git a/core/java/android/server/BluetoothA2dpService.java b/core/java/android/server/BluetoothA2dpService.java index ac89934..a52a221 100644 --- a/core/java/android/server/BluetoothA2dpService.java +++ b/core/java/android/server/BluetoothA2dpService.java @@ -27,7 +27,6 @@ import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothDevice; import android.bluetooth.BluetoothUuid; import android.bluetooth.IBluetoothA2dp; -import android.os.ParcelUuid; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; @@ -35,6 +34,7 @@ import android.content.IntentFilter; import android.media.AudioManager; import android.os.Handler; import android.os.Message; +import android.os.ParcelUuid; import android.provider.Settings; import android.util.Log; @@ -55,8 +55,6 @@ 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 String PROPERTY_STATE = "State"; private static final String SINK_STATE_DISCONNECTED = "disconnected"; @@ -73,6 +71,7 @@ public class BluetoothA2dpService extends IBluetoothA2dp.Stub { private final BluetoothService mBluetoothService; private final BluetoothAdapter mAdapter; private int mTargetA2dpState; + private boolean mAdjustedPriority = false; private final BroadcastReceiver mReceiver = new BroadcastReceiver() { @Override @@ -104,16 +103,6 @@ public class BluetoothA2dpService extends IBluetoothA2dp.Stub { setSinkPriority(device, BluetoothA2dp.PRIORITY_UNDEFINED); break; } - } else if (action.equals(BluetoothDevice.ACTION_ACL_CONNECTED)) { - if (getSinkPriority(device) == BluetoothA2dp.PRIORITY_AUTO_CONNECT && - isSinkDevice(device)) { - // This device is a preferred sink. Make an A2DP connection - // after a delay. We delay to avoid connection collisions, - // and to give other profiles such as HFP a chance to - // connect first. - Message msg = Message.obtain(mHandler, MESSAGE_CONNECT_TO, device); - mHandler.sendMessageDelayed(msg, 6000); - } } else if (action.equals(BluetoothDevice.ACTION_ACL_DISCONNECTED)) { synchronized (this) { if (mAudioDevices.containsKey(device)) { @@ -187,6 +176,7 @@ public class BluetoothA2dpService extends IBluetoothA2dp.Stub { if (mBluetoothService.isEnabled()) onBluetoothEnable(); mTargetA2dpState = -1; + mBluetoothService.setA2dpService(this); } @Override @@ -198,29 +188,6 @@ public class BluetoothA2dpService extends IBluetoothA2dp.Stub { } } - private final Handler mHandler = new Handler() { - @Override - public void handleMessage(Message msg) { - switch (msg.what) { - case MESSAGE_CONNECT_TO: - BluetoothDevice device = (BluetoothDevice) msg.obj; - // check bluetooth is still on, device is still preferred, and - // nothing is currently connected - if (mBluetoothService.isEnabled() && - getSinkPriority(device) == BluetoothA2dp.PRIORITY_AUTO_CONNECT && - lookupSinksMatchingStates(new int[] { - BluetoothA2dp.STATE_CONNECTING, - BluetoothA2dp.STATE_CONNECTED, - BluetoothA2dp.STATE_PLAYING, - BluetoothA2dp.STATE_DISCONNECTING}).size() == 0) { - log("Auto-connecting A2DP to sink " + device); - connectSink(device); - } - break; - } - } - }; - private int convertBluezSinkStringtoState(String value) { if (value.equalsIgnoreCase("disconnected")) return BluetoothA2dp.STATE_DISCONNECTED; @@ -308,13 +275,37 @@ public class BluetoothA2dpService extends IBluetoothA2dp.Stub { mAudioManager.setParameters(BLUETOOTH_ENABLED + "=false"); } + private synchronized boolean isConnectSinkFeasible(BluetoothDevice device) { + if (!mBluetoothService.isEnabled() || !isSinkDevice(device) || + getSinkPriority(device) == BluetoothA2dp.PRIORITY_OFF) { + return false; + } + + if (mAudioDevices.get(device) == null && !addAudioSink(device)) { + return false; + } + + String path = mBluetoothService.getObjectPathFromAddress(device.getAddress()); + if (path == null) { + return false; + } + return true; + } + public synchronized boolean connectSink(BluetoothDevice device) { mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, "Need BLUETOOTH_ADMIN permission"); if (DBG) log("connectSink(" + device + ")"); + if (!isConnectSinkFeasible(device)) return false; + return mBluetoothService.connectSink(device.getAddress()); + } + + public synchronized boolean connectSinkInternal(BluetoothDevice device) { if (!mBluetoothService.isEnabled()) return false; + int state = mAudioDevices.get(device); + // ignore if there are any active sinks if (lookupSinksMatchingStates(new int[] { BluetoothA2dp.STATE_CONNECTING, @@ -324,11 +315,6 @@ public class BluetoothA2dpService extends IBluetoothA2dp.Stub { return false; } - if (mAudioDevices.get(device) == null && !addAudioSink(device)) - return false; - - int state = mAudioDevices.get(device); - switch (state) { case BluetoothA2dp.STATE_CONNECTED: case BluetoothA2dp.STATE_PLAYING: @@ -339,8 +325,6 @@ public class BluetoothA2dpService extends IBluetoothA2dp.Stub { } String path = mBluetoothService.getObjectPathFromAddress(device.getAddress()); - if (path == null) - return false; // State is DISCONNECTED handleSinkStateChange(device, state, BluetoothA2dp.STATE_CONNECTING); @@ -353,11 +337,7 @@ public class BluetoothA2dpService extends IBluetoothA2dp.Stub { return true; } - public synchronized boolean disconnectSink(BluetoothDevice device) { - mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, - "Need BLUETOOTH_ADMIN permission"); - if (DBG) log("disconnectSink(" + device + ")"); - + private synchronized boolean isDisconnectSinkFeasible(BluetoothDevice device) { String path = mBluetoothService.getObjectPathFromAddress(device.getAddress()); if (path == null) { return false; @@ -370,6 +350,20 @@ public class BluetoothA2dpService extends IBluetoothA2dp.Stub { case BluetoothA2dp.STATE_DISCONNECTING: return true; } + return true; + } + + public synchronized boolean disconnectSink(BluetoothDevice device) { + mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, + "Need BLUETOOTH_ADMIN permission"); + if (DBG) log("disconnectSink(" + device + ")"); + if (!isDisconnectSinkFeasible(device)) return false; + return mBluetoothService.disconnectSink(device.getAddress()); + } + + public synchronized boolean disconnectSinkInternal(BluetoothDevice device) { + int state = getSinkState(device); + String path = mBluetoothService.getObjectPathFromAddress(device.getAddress()); // State is CONNECTING or CONNECTED or PLAYING handleSinkStateChange(device, state, BluetoothA2dp.STATE_DISCONNECTING); @@ -504,6 +498,12 @@ public class BluetoothA2dpService extends IBluetoothA2dp.Stub { setSinkPriority(device, BluetoothA2dp.PRIORITY_AUTO_CONNECT); } + if (state == BluetoothA2dp.STATE_CONNECTED) { + // We will only have 1 device with AUTO_CONNECT priority + // To be backward compatible set everyone else to have PRIORITY_ON + adjustOtherSinkPriorities(device); + } + Intent intent = new Intent(BluetoothA2dp.ACTION_SINK_STATE_CHANGED); intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device); intent.putExtra(BluetoothA2dp.EXTRA_PREVIOUS_SINK_STATE, prevState); @@ -514,6 +514,18 @@ public class BluetoothA2dpService extends IBluetoothA2dp.Stub { } } + private void adjustOtherSinkPriorities(BluetoothDevice connectedDevice) { + if (!mAdjustedPriority) { + for (BluetoothDevice device : mAdapter.getBondedDevices()) { + if (getSinkPriority(device) >= BluetoothA2dp.PRIORITY_AUTO_CONNECT && + !device.equals(connectedDevice)) { + setSinkPriority(device, BluetoothA2dp.PRIORITY_ON); + } + } + mAdjustedPriority = true; + } + } + private synchronized Set<BluetoothDevice> lookupSinksMatchingStates(int[] states) { Set<BluetoothDevice> sinks = new HashSet<BluetoothDevice>(); if (mAudioDevices.isEmpty()) { diff --git a/core/java/android/server/BluetoothEventLoop.java b/core/java/android/server/BluetoothEventLoop.java index c0e4600..e1d3f13 100644 --- a/core/java/android/server/BluetoothEventLoop.java +++ b/core/java/android/server/BluetoothEventLoop.java @@ -566,6 +566,7 @@ class BluetoothEventLoop { authorized = a2dp.getSinkPriority(device) > BluetoothA2dp.PRIORITY_OFF; if (authorized) { Log.i(TAG, "Allowing incoming A2DP / AVRCP connection from " + address); + mBluetoothService.notifyIncomingA2dpConnection(address); } else { Log.i(TAG, "Rejecting incoming A2DP / AVRCP connection from " + address); } diff --git a/core/java/android/server/BluetoothService.java b/core/java/android/server/BluetoothService.java index c0affd3..fc40770 100644 --- a/core/java/android/server/BluetoothService.java +++ b/core/java/android/server/BluetoothService.java @@ -28,6 +28,7 @@ import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothClass; import android.bluetooth.BluetoothDevice; import android.bluetooth.BluetoothHeadset; +import android.bluetooth.BluetoothProfileConnectionState; import android.bluetooth.BluetoothSocket; import android.bluetooth.BluetoothUuid; import android.bluetooth.IBluetooth; @@ -112,7 +113,7 @@ public class BluetoothService extends IBluetooth.Stub { BluetoothUuid.HSP, BluetoothUuid.ObexObjectPush }; - + // TODO(): Optimize all these string handling private final Map<String, String> mAdapterProperties; private final HashMap<String, Map<String, String>> mDeviceProperties; @@ -122,6 +123,9 @@ public class BluetoothService extends IBluetooth.Stub { private final HashMap<Integer, Integer> mServiceRecordToPid; + private final HashMap<String, BluetoothProfileConnectionState> mProfileConnectionMgr; + + private BluetoothA2dpService mA2dpService; private static String mDockAddress; private String mDockPin; @@ -179,6 +183,7 @@ public class BluetoothService extends IBluetooth.Stub { mUuidIntentTracker = new ArrayList<String>(); mUuidCallbackTracker = new HashMap<RemoteService, IBluetoothCallback>(); mServiceRecordToPid = new HashMap<Integer, Integer>(); + mProfileConnectionMgr = new HashMap<String, BluetoothProfileConnectionState>(); IntentFilter filter = new IntentFilter(); registerForAirplaneMode(filter); @@ -187,7 +192,7 @@ public class BluetoothService extends IBluetooth.Stub { mContext.registerReceiver(mReceiver, filter); } - public static synchronized String readDockBluetoothAddress() { + public static synchronized String readDockBluetoothAddress() { if (mDockAddress != null) return mDockAddress; BufferedInputStream file = null; @@ -534,6 +539,7 @@ public class BluetoothService extends IBluetooth.Stub { mIsDiscovering = false; mBondState.readAutoPairingData(); mBondState.loadBondState(); + initProfileState(); mHandler.sendMessageDelayed( mHandler.obtainMessage(MESSAGE_REGISTER_SDP_RECORDS, 1, -1), 3000); @@ -648,6 +654,12 @@ public class BluetoothService extends IBluetooth.Stub { } } + if (state == BluetoothDevice.BOND_BONDED) { + addProfileState(address); + } else if (state == BluetoothDevice.BOND_NONE) { + removeProfileState(address); + } + if (DBG) log(address + " bond state " + oldState + " -> " + state + " (" + reason + ")"); Intent intent = new Intent(BluetoothDevice.ACTION_BOND_STATE_CHANGED); @@ -1167,6 +1179,16 @@ public class BluetoothService extends IBluetooth.Stub { if (!BluetoothAdapter.checkBluetoothAddress(address)) { return false; } + BluetoothProfileConnectionState state = mProfileConnectionMgr.get(address); + if (state != null) { + state.sendMessage(BluetoothProfileConnectionState.UNPAIR); + return true; + } else { + return false; + } + } + + public synchronized boolean removeBondInternal(String address) { return removeDeviceNative(getObjectPathFromAddress(address)); } @@ -1919,6 +1941,104 @@ public class BluetoothService extends IBluetooth.Stub { if (!result) log("Set Link Timeout to:" + num_slots + " slots failed"); } + public boolean connectHeadset(String address) { + BluetoothProfileConnectionState state = mProfileConnectionMgr.get(address); + if (state != null) { + state.sendMessage(BluetoothProfileConnectionState.CONNECT_HFP_OUTGOING); + return true; + } + return false; + } + + public boolean disconnectHeadset(String address) { + BluetoothProfileConnectionState state = mProfileConnectionMgr.get(address); + if (state != null) { + state.sendMessage(BluetoothProfileConnectionState.DISCONNECT_HFP_OUTGOING); + return true; + } + return false; + } + + public boolean connectSink(String address) { + BluetoothProfileConnectionState state = mProfileConnectionMgr.get(address); + if (state != null) { + state.sendMessage(BluetoothProfileConnectionState.CONNECT_A2DP_OUTGOING); + return true; + } + return false; + } + + public boolean disconnectSink(String address) { + BluetoothProfileConnectionState state = mProfileConnectionMgr.get(address); + if (state != null) { + state.sendMessage(BluetoothProfileConnectionState.DISCONNECT_A2DP_OUTGOING); + return true; + } + return false; + } + + private BluetoothProfileConnectionState addProfileState(String address) { + BluetoothProfileConnectionState state = mProfileConnectionMgr.get(address); + if (state != null) return state; + + state = new BluetoothProfileConnectionState(mContext, address, this, mA2dpService); + mProfileConnectionMgr.put(address, state); + state.start(); + return state; + } + + private void removeProfileState(String address) { + mProfileConnectionMgr.remove(address); + } + + private void initProfileState() { + String []bonds = null; + String val = getPropertyInternal("Devices"); + if (val != null) { + bonds = val.split(","); + } + if (bonds == null) { + return; + } + + for (String path : bonds) { + String address = getAddressFromObjectPath(path); + BluetoothProfileConnectionState state = addProfileState(address); + // Allow 8 secs for SDP records to get registered. + Message msg = new Message(); + msg.what = BluetoothProfileConnectionState.AUTO_CONNECT_PROFILES; + state.sendMessageDelayed(msg, 8000); + } + } + + public boolean notifyIncomingConnection(String address) { + BluetoothProfileConnectionState state = + mProfileConnectionMgr.get(address); + if (state != null) { + Message msg = new Message(); + msg.what = BluetoothProfileConnectionState.CONNECT_HFP_INCOMING; + state.sendMessage(msg); + return true; + } + return false; + } + + /*package*/ boolean notifyIncomingA2dpConnection(String address) { + BluetoothProfileConnectionState state = + mProfileConnectionMgr.get(address); + if (state != null) { + Message msg = new Message(); + msg.what = BluetoothProfileConnectionState.CONNECT_A2DP_INCOMING; + state.sendMessage(msg); + return true; + } + return false; + } + + /*package*/ void setA2dpService(BluetoothA2dpService a2dpService) { + mA2dpService = a2dpService; + } + private static void log(String msg) { Log.d(TAG, msg); } |