diff options
Diffstat (limited to 'src/com/android/nfc/handover/BluetoothHeadsetHandover.java')
-rw-r--r-- | src/com/android/nfc/handover/BluetoothHeadsetHandover.java | 231 |
1 files changed, 161 insertions, 70 deletions
diff --git a/src/com/android/nfc/handover/BluetoothHeadsetHandover.java b/src/com/android/nfc/handover/BluetoothHeadsetHandover.java index 7974dfa..1377160 100644 --- a/src/com/android/nfc/handover/BluetoothHeadsetHandover.java +++ b/src/com/android/nfc/handover/BluetoothHeadsetHandover.java @@ -16,6 +16,7 @@ package com.android.nfc.handover; +import android.app.ActivityManager; import android.bluetooth.BluetoothA2dp; import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothDevice; @@ -28,6 +29,7 @@ import android.content.IntentFilter; import android.os.Handler; import android.os.Looper; import android.os.Message; +import android.os.UserHandle; import android.util.Log; import android.view.KeyEvent; import android.widget.Toast; @@ -44,9 +46,8 @@ import com.android.nfc.R; * designed to be re-used after the sequence has completed or timed out. * Subsequent NFC interactions should use new objects. * - * TODO: UI review */ -public class BluetoothHeadsetHandover { +public class BluetoothHeadsetHandover implements BluetoothProfile.ServiceListener { static final String TAG = HandoverManager.TAG; static final boolean DBG = HandoverManager.DBG; @@ -57,28 +58,33 @@ public class BluetoothHeadsetHandover { static final int STATE_INIT = 0; static final int STATE_TURNING_ON = 1; - static final int STATE_WAITING_FOR_BOND_CONFIRMATION = 2; - static final int STATE_BONDING = 3; - static final int STATE_CONNECTING = 4; - static final int STATE_DISCONNECTING = 5; - static final int STATE_COMPLETE = 6; + static final int STATE_WAITING_FOR_PROXIES = 2; + static final int STATE_INIT_COMPLETE = 3; + static final int STATE_WAITING_FOR_BOND_CONFIRMATION = 4; + static final int STATE_BONDING = 5; + static final int STATE_CONNECTING = 6; + static final int STATE_DISCONNECTING = 7; + static final int STATE_COMPLETE = 8; static final int RESULT_PENDING = 0; static final int RESULT_CONNECTED = 1; static final int RESULT_DISCONNECTED = 2; + static final int ACTION_INIT = 0; static final int ACTION_DISCONNECT = 1; static final int ACTION_CONNECT = 2; static final int MSG_TIMEOUT = 1; + static final int MSG_NEXT_STEP = 2; final Context mContext; final BluetoothDevice mDevice; final String mName; final HandoverPowerManager mHandoverPowerManager; - final BluetoothA2dp mA2dp; - final BluetoothHeadset mHeadset; final Callback mCallback; + final BluetoothAdapter mBluetoothAdapter; + + final Object mLock = new Object(); // only used on main thread int mAction; @@ -86,21 +92,24 @@ public class BluetoothHeadsetHandover { int mHfpResult; // used only in STATE_CONNECTING and STATE_DISCONNETING int mA2dpResult; // used only in STATE_CONNECTING and STATE_DISCONNETING + // protected by mLock + BluetoothA2dp mA2dp; + BluetoothHeadset mHeadset; + public interface Callback { public void onBluetoothHeadsetHandoverComplete(boolean connected); } public BluetoothHeadsetHandover(Context context, BluetoothDevice device, String name, - HandoverPowerManager powerManager, BluetoothA2dp a2dp, BluetoothHeadset headset, - Callback callback) { + HandoverPowerManager powerManager, Callback callback) { checkMainThread(); // mHandler must get get constructed on Main Thread for toasts to work mContext = context; mDevice = device; mName = name; mHandoverPowerManager = powerManager; - mA2dp = a2dp; - mHeadset = headset; mCallback = callback; + mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter(); + mState = STATE_INIT; } @@ -111,6 +120,7 @@ public class BluetoothHeadsetHandover { public void start() { checkMainThread(); if (mState != STATE_INIT) return; + if (mBluetoothAdapter == null) return; IntentFilter filter = new IntentFilter(); filter.addAction(BluetoothAdapter.ACTION_STATE_CHANGED); @@ -122,15 +132,8 @@ public class BluetoothHeadsetHandover { mContext.registerReceiver(mReceiver, filter); - if (mA2dp.getConnectedDevices().contains(mDevice) || - mHeadset.getConnectedDevices().contains(mDevice)) { - Log.i(TAG, "ACTION_DISCONNECT addr=" + mDevice + " name=" + mName); - mAction = ACTION_DISCONNECT; - } else { - Log.i(TAG, "ACTION_CONNECT addr=" + mDevice + " name=" + mName); - mAction = ACTION_CONNECT; - } mHandler.sendMessageDelayed(mHandler.obtainMessage(MSG_TIMEOUT), TIMEOUT_MS); + mAction = ACTION_INIT; nextStep(); } @@ -138,35 +141,83 @@ public class BluetoothHeadsetHandover { * Called to execute next step in state machine */ void nextStep() { - if (mAction == ACTION_CONNECT) { + if (mAction == ACTION_INIT) { + nextStepInit(); + } else if (mAction == ACTION_CONNECT) { nextStepConnect(); } else { nextStepDisconnect(); } } - void nextStepDisconnect() { + /* + * Enables bluetooth and gets the profile proxies + */ + void nextStepInit() { switch (mState) { case STATE_INIT: - mState = STATE_DISCONNECTING; - if (mHeadset.getConnectionState(mDevice) != BluetoothProfile.STATE_DISCONNECTED) { - mHfpResult = RESULT_PENDING; - mHeadset.disconnect(mDevice); - } else { - mHfpResult = RESULT_DISCONNECTED; - } - if (mA2dp.getConnectionState(mDevice) != BluetoothProfile.STATE_DISCONNECTED) { - mA2dpResult = RESULT_PENDING; - mA2dp.disconnect(mDevice); - } else { - mA2dpResult = RESULT_DISCONNECTED; + if (!mHandoverPowerManager.isBluetoothEnabled()) { + if (mHandoverPowerManager.enableBluetooth()) { + // Bluetooth is being enabled + mState = STATE_TURNING_ON; + } else { + toast(mContext.getString(R.string.failed_to_enable_bt)); + complete(false); + } + break; } - if (mA2dpResult == RESULT_PENDING || mHfpResult == RESULT_PENDING) { - toast(mContext.getString(R.string.disconnecting_headset ) + " " + - mName + "..."); + // fall-through + case STATE_TURNING_ON: + if (mA2dp == null || mHeadset == null) { + mState = STATE_WAITING_FOR_PROXIES; + if (!getProfileProxys()) { + complete(false); + } break; } // fall-through + case STATE_WAITING_FOR_PROXIES: + mState = STATE_INIT_COMPLETE; + // Check connected devices and see if we need to disconnect + synchronized(mLock) { + if (mA2dp.getConnectedDevices().contains(mDevice) || + mHeadset.getConnectedDevices().contains(mDevice)) { + Log.i(TAG, "ACTION_DISCONNECT addr=" + mDevice + " name=" + mName); + mAction = ACTION_DISCONNECT; + } else { + Log.i(TAG, "ACTION_CONNECT addr=" + mDevice + " name=" + mName); + mAction = ACTION_CONNECT; + } + } + nextStep(); + } + + } + + void nextStepDisconnect() { + switch (mState) { + case STATE_INIT_COMPLETE: + mState = STATE_DISCONNECTING; + synchronized (mLock) { + if (mHeadset.getConnectionState(mDevice) != BluetoothProfile.STATE_DISCONNECTED) { + mHfpResult = RESULT_PENDING; + mHeadset.disconnect(mDevice); + } else { + mHfpResult = RESULT_DISCONNECTED; + } + if (mA2dp.getConnectionState(mDevice) != BluetoothProfile.STATE_DISCONNECTED) { + mA2dpResult = RESULT_PENDING; + mA2dp.disconnect(mDevice); + } else { + mA2dpResult = RESULT_DISCONNECTED; + } + if (mA2dpResult == RESULT_PENDING || mHfpResult == RESULT_PENDING) { + toast(mContext.getString(R.string.disconnecting_headset ) + " " + + mName + "..."); + break; + } + } + // fall-through case STATE_DISCONNECTING: if (mA2dpResult == RESULT_PENDING || mHfpResult == RESULT_PENDING) { // still disconnecting @@ -178,26 +229,26 @@ public class BluetoothHeadsetHandover { complete(false); break; } + + } + + boolean getProfileProxys() { + if(!mBluetoothAdapter.getProfileProxy(mContext, this, BluetoothProfile.HEADSET)) + return false; + + if(!mBluetoothAdapter.getProfileProxy(mContext, this, BluetoothProfile.A2DP)) + return false; + + return true; } void nextStepConnect() { switch (mState) { - case STATE_INIT: - if (!mHandoverPowerManager.isBluetoothEnabled()) { - if (mHandoverPowerManager.enableBluetooth()) { - // Bluetooth is being enabled - mState = STATE_TURNING_ON; - } else { - toast(mContext.getString(R.string.failed_to_enable_bt)); - complete(false); - } - break; - } - // fall-through - case STATE_TURNING_ON: + case STATE_INIT_COMPLETE: if (mDevice.getBondState() != BluetoothDevice.BOND_BONDED) { requestPairConfirmation(); mState = STATE_WAITING_FOR_BOND_CONFIRMATION; + break; } // fall-through @@ -211,21 +262,23 @@ public class BluetoothHeadsetHandover { // Bluetooth Profile service will correctly serialize // HFP then A2DP connect mState = STATE_CONNECTING; - if (mHeadset.getConnectionState(mDevice) != BluetoothProfile.STATE_CONNECTED) { - mHfpResult = RESULT_PENDING; - mHeadset.connect(mDevice); - } else { - mHfpResult = RESULT_CONNECTED; - } - if (mA2dp.getConnectionState(mDevice) != BluetoothProfile.STATE_CONNECTED) { - mA2dpResult = RESULT_PENDING; - mA2dp.connect(mDevice); - } else { - mA2dpResult = RESULT_CONNECTED; - } - if (mA2dpResult == RESULT_PENDING || mHfpResult == RESULT_PENDING) { - toast(mContext.getString(R.string.connecting_headset) + " " + mName + "..."); - break; + synchronized (mLock) { + if (mHeadset.getConnectionState(mDevice) != BluetoothProfile.STATE_CONNECTED) { + mHfpResult = RESULT_PENDING; + mHeadset.connect(mDevice); + } else { + mHfpResult = RESULT_CONNECTED; + } + if (mA2dp.getConnectionState(mDevice) != BluetoothProfile.STATE_CONNECTED) { + mA2dpResult = RESULT_PENDING; + mA2dp.connect(mDevice); + } else { + mA2dpResult = RESULT_CONNECTED; + } + if (mA2dpResult == RESULT_PENDING || mHfpResult == RESULT_PENDING) { + toast(mContext.getString(R.string.connecting_headset) + " " + mName + "..."); + break; + } } // fall-through case STATE_CONNECTING: @@ -260,7 +313,7 @@ public class BluetoothHeadsetHandover { if (BluetoothAdapter.ACTION_STATE_CHANGED.equals(action) && mState == STATE_TURNING_ON) { int state = intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, BluetoothAdapter.ERROR); if (state == BluetoothAdapter.STATE_ON) { - nextStepConnect(); + nextStep(); } else if (state == BluetoothAdapter.STATE_OFF) { toast(mContext.getString(R.string.failed_to_enable_bt)); complete(false); @@ -313,6 +366,16 @@ public class BluetoothHeadsetHandover { mState = STATE_COMPLETE; mContext.unregisterReceiver(mReceiver); mHandler.removeMessages(MSG_TIMEOUT); + synchronized (mLock) { + if (mA2dp != null) { + mBluetoothAdapter.closeProfileProxy(BluetoothProfile.A2DP, mA2dp); + } + if (mHeadset != null) { + mBluetoothAdapter.closeProfileProxy(BluetoothProfile.HEADSET, mHeadset); + } + mA2dp = null; + mHeadset = null; + } mCallback.onBluetoothHeadsetHandoverComplete(connected); } @@ -324,10 +387,10 @@ public class BluetoothHeadsetHandover { Intent intent = new Intent(Intent.ACTION_MEDIA_BUTTON); intent.putExtra(Intent.EXTRA_KEY_EVENT, new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_MEDIA_PLAY)); - mContext.sendOrderedBroadcast(intent, null); + mContext.sendOrderedBroadcastAsUser(intent, UserHandle.CURRENT, null, null, null, 0, null, null); intent.putExtra(Intent.EXTRA_KEY_EVENT, new KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_MEDIA_PLAY)); - mContext.sendOrderedBroadcast(intent, null); + mContext.sendOrderedBroadcastAsUser(intent, UserHandle.CURRENT, null, null, null, 0, null, null); } void requestPairConfirmation() { @@ -336,7 +399,7 @@ public class BluetoothHeadsetHandover { dialogIntent.putExtra(BluetoothDevice.EXTRA_DEVICE, mDevice); - mContext.startActivity(dialogIntent); + mContext.startActivityAsUser(dialogIntent, new UserHandle(UserHandle.USER_CURRENT)); } final Handler mHandler = new Handler() { @@ -348,6 +411,9 @@ public class BluetoothHeadsetHandover { Log.i(TAG, "Timeout completing BT handover"); complete(false); break; + case MSG_NEXT_STEP: + nextStep(); + break; } } }; @@ -364,4 +430,29 @@ public class BluetoothHeadsetHandover { throw new IllegalThreadStateException("must be called on main thread"); } } + + @Override + public void onServiceConnected(int profile, BluetoothProfile proxy) { + synchronized (mLock) { + switch (profile) { + case BluetoothProfile.HEADSET: + mHeadset = (BluetoothHeadset) proxy; + if (mA2dp != null) { + mHandler.sendEmptyMessage(MSG_NEXT_STEP); + } + break; + case BluetoothProfile.A2DP: + mA2dp = (BluetoothA2dp) proxy; + if (mHeadset != null) { + mHandler.sendEmptyMessage(MSG_NEXT_STEP); + } + break; + } + } + } + + @Override + public void onServiceDisconnected(int profile) { + // We can ignore these + } } |