summaryrefslogtreecommitdiffstats
path: root/core/java/android/server
diff options
context:
space:
mode:
authorJaikumar Ganesh <jaikumar@google.com>2010-06-02 14:36:14 -0700
committerJaikumar Ganesh <jaikumar@google.com>2010-06-02 16:03:45 -0700
commit9b637e5985f9a86f39d70335c0390ade3716592a (patch)
tree1ffa5bfee87452ebdb619a075fab765714e916d3 /core/java/android/server
parentc87d5849f22d805b3dde8f70f60ddfadfc7c7b9d (diff)
downloadframeworks_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.java108
-rw-r--r--core/java/android/server/BluetoothEventLoop.java1
-rw-r--r--core/java/android/server/BluetoothService.java124
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);
}