BluetoothPeripheralHandover.java revision d82d9db81f30ccecd29a0531e6db9b49c9c2cd95
1a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly/* 2a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly * Copyright (C) 2012 The Android Open Source Project 3a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly * 4a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly * Licensed under the Apache License, Version 2.0 (the "License"); 5a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly * you may not use this file except in compliance with the License. 6a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly * You may obtain a copy of the License at 7a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly * 8a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly * http://www.apache.org/licenses/LICENSE-2.0 9a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly * 10a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly * Unless required by applicable law or agreed to in writing, software 11a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly * distributed under the License is distributed on an "AS IS" BASIS, 12a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly * See the License for the specific language governing permissions and 14a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly * limitations under the License. 15a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly */ 16a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly 17a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pellypackage com.android.nfc.handover; 18a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly 19a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pellyimport android.bluetooth.BluetoothA2dp; 20a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pellyimport android.bluetooth.BluetoothAdapter; 21a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pellyimport android.bluetooth.BluetoothDevice; 22a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pellyimport android.bluetooth.BluetoothHeadset; 23a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pellyimport android.bluetooth.BluetoothProfile; 24a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pellyimport android.content.BroadcastReceiver; 25a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pellyimport android.content.Context; 26a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pellyimport android.content.Intent; 27a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pellyimport android.content.IntentFilter; 28a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pellyimport android.os.Handler; 29a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pellyimport android.os.Looper; 30a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pellyimport android.os.Message; 31a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pellyimport android.util.Log; 32a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pellyimport android.view.KeyEvent; 33a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pellyimport android.widget.Toast; 34a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly 35226307d8c2952c42855005d7c0107b42b066bc9aMartijn Coenenimport com.android.nfc.handover.HandoverManager.HandoverPowerManager; 36d82d9db81f30ccecd29a0531e6db9b49c9c2cd95Martijn Coenenimport com.android.nfc.R; 37226307d8c2952c42855005d7c0107b42b066bc9aMartijn Coenen 38a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly/** 39a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly * Connects / Disconnects from a Bluetooth headset (or any device that 40a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly * might implement BT HSP, HFP or A2DP sink) when touched with NFC. 41a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly * 42a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly * This object is created on an NFC interaction, and determines what 43a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly * sequence of Bluetooth actions to take, and executes them. It is not 44a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly * designed to be re-used after the sequence has completed or timed out. 45a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly * Subsequent NFC interactions should use new objects. 46a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly * 47d82d9db81f30ccecd29a0531e6db9b49c9c2cd95Martijn Coenen * TODO: UI review 48a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly */ 49a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pellypublic class BluetoothHeadsetHandover { 50a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly static final String TAG = HandoverManager.TAG; 51a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly static final boolean DBG = HandoverManager.DBG; 52a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly 53a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly static final int TIMEOUT_MS = 20000; 54a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly 55a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly static final int STATE_INIT = 0; 56a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly static final int STATE_TURNING_ON = 1; 57a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly static final int STATE_BONDING = 2; 58a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly static final int STATE_CONNECTING = 3; 59a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly static final int STATE_DISCONNECTING = 4; 60a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly static final int STATE_COMPLETE = 5; 61a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly 62a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly static final int RESULT_PENDING = 0; 63a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly static final int RESULT_CONNECTED = 1; 64a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly static final int RESULT_DISCONNECTED = 2; 65a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly 66a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly static final int ACTION_DISCONNECT = 1; 67a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly static final int ACTION_CONNECT = 2; 68a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly 6949913d6a0ed709ff6edde40bb040605997b561f3Nick Pelly static final int MSG_TIMEOUT = 1; 70a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly 71a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly final Context mContext; 72a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly final BluetoothDevice mDevice; 73a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly final String mName; 74226307d8c2952c42855005d7c0107b42b066bc9aMartijn Coenen final HandoverPowerManager mHandoverPowerManager; 75a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly final BluetoothA2dp mA2dp; 76a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly final BluetoothHeadset mHeadset; 77a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly final Callback mCallback; 78a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly 7949913d6a0ed709ff6edde40bb040605997b561f3Nick Pelly // only used on main thread 80a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly int mAction; 81a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly int mState; 82a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly int mHfpResult; // used only in STATE_CONNECTING and STATE_DISCONNETING 83a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly int mA2dpResult; // used only in STATE_CONNECTING and STATE_DISCONNETING 84a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly 85a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly public interface Callback { 86226307d8c2952c42855005d7c0107b42b066bc9aMartijn Coenen public void onBluetoothHeadsetHandoverComplete(boolean connected); 87a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly } 88a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly 89a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly public BluetoothHeadsetHandover(Context context, BluetoothDevice device, String name, 90226307d8c2952c42855005d7c0107b42b066bc9aMartijn Coenen HandoverPowerManager powerManager, BluetoothA2dp a2dp, BluetoothHeadset headset, 91a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly Callback callback) { 9249913d6a0ed709ff6edde40bb040605997b561f3Nick Pelly checkMainThread(); // mHandler must get get constructed on Main Thread for toasts to work 93a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly mContext = context; 94a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly mDevice = device; 95a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly mName = name; 96226307d8c2952c42855005d7c0107b42b066bc9aMartijn Coenen mHandoverPowerManager = powerManager; 97a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly mA2dp = a2dp; 98a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly mHeadset = headset; 99a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly mCallback = callback; 100a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly mState = STATE_INIT; 101a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly } 102a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly 103a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly /** 104a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly * Main entry point. This method is usually called after construction, 10549913d6a0ed709ff6edde40bb040605997b561f3Nick Pelly * to begin the BT sequence. Must be called on Main thread. 106a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly */ 10749913d6a0ed709ff6edde40bb040605997b561f3Nick Pelly public void start() { 10849913d6a0ed709ff6edde40bb040605997b561f3Nick Pelly checkMainThread(); 10949913d6a0ed709ff6edde40bb040605997b561f3Nick Pelly if (mState != STATE_INIT) return; 11049913d6a0ed709ff6edde40bb040605997b561f3Nick Pelly 111a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly IntentFilter filter = new IntentFilter(); 112a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly filter.addAction(BluetoothAdapter.ACTION_STATE_CHANGED); 113a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly filter.addAction(BluetoothDevice.ACTION_BOND_STATE_CHANGED); 114a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly filter.addAction(BluetoothA2dp.ACTION_CONNECTION_STATE_CHANGED); 115a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly filter.addAction(BluetoothHeadset.ACTION_CONNECTION_STATE_CHANGED); 116a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly mContext.registerReceiver(mReceiver, filter); 117a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly 118a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly if (mA2dp.getConnectedDevices().contains(mDevice) || 119a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly mHeadset.getConnectedDevices().contains(mDevice)) { 120a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly Log.i(TAG, "ACTION_DISCONNECT addr=" + mDevice + " name=" + mName); 121a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly mAction = ACTION_DISCONNECT; 122a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly } else { 123a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly Log.i(TAG, "ACTION_CONNECT addr=" + mDevice + " name=" + mName); 124a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly mAction = ACTION_CONNECT; 125a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly } 126a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly mHandler.sendMessageDelayed(mHandler.obtainMessage(MSG_TIMEOUT), TIMEOUT_MS); 127a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly nextStep(); 128a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly } 129a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly 130a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly /** 131a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly * Called to execute next step in state machine 132a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly */ 13349913d6a0ed709ff6edde40bb040605997b561f3Nick Pelly void nextStep() { 134a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly if (mAction == ACTION_CONNECT) { 135a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly nextStepConnect(); 136a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly } else { 137a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly nextStepDisconnect(); 138a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly } 139a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly } 140a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly 14149913d6a0ed709ff6edde40bb040605997b561f3Nick Pelly void nextStepDisconnect() { 142a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly switch (mState) { 143a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly case STATE_INIT: 144a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly mState = STATE_DISCONNECTING; 145a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly if (mHeadset.getConnectionState(mDevice) != BluetoothProfile.STATE_DISCONNECTED) { 146a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly mHfpResult = RESULT_PENDING; 147a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly mHeadset.disconnect(mDevice); 148a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly } else { 149a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly mHfpResult = RESULT_DISCONNECTED; 150a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly } 151a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly if (mA2dp.getConnectionState(mDevice) != BluetoothProfile.STATE_DISCONNECTED) { 152a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly mA2dpResult = RESULT_PENDING; 153a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly mA2dp.disconnect(mDevice); 154a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly } else { 155a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly mA2dpResult = RESULT_DISCONNECTED; 156a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly } 157a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly if (mA2dpResult == RESULT_PENDING || mHfpResult == RESULT_PENDING) { 158d82d9db81f30ccecd29a0531e6db9b49c9c2cd95Martijn Coenen toast(mContext.getString(R.string.disconnecting_headset ) + " " + 159d82d9db81f30ccecd29a0531e6db9b49c9c2cd95Martijn Coenen mName + "..."); 160a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly break; 161a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly } 162a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly // fall-through 163a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly case STATE_DISCONNECTING: 164a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly if (mA2dpResult == RESULT_PENDING || mHfpResult == RESULT_PENDING) { 165a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly // still disconnecting 166a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly break; 167a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly } 168a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly if (mA2dpResult == RESULT_DISCONNECTED && mHfpResult == RESULT_DISCONNECTED) { 169d82d9db81f30ccecd29a0531e6db9b49c9c2cd95Martijn Coenen toast(mContext.getString(R.string.disconnected_headset) + " " + mName); 170a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly } 171226307d8c2952c42855005d7c0107b42b066bc9aMartijn Coenen complete(false); 172a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly break; 173a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly } 174a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly } 175a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly 17649913d6a0ed709ff6edde40bb040605997b561f3Nick Pelly void nextStepConnect() { 177a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly switch (mState) { 178a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly case STATE_INIT: 179226307d8c2952c42855005d7c0107b42b066bc9aMartijn Coenen if (!mHandoverPowerManager.isBluetoothEnabled()) { 180226307d8c2952c42855005d7c0107b42b066bc9aMartijn Coenen if (mHandoverPowerManager.enableBluetooth()) { 181226307d8c2952c42855005d7c0107b42b066bc9aMartijn Coenen // Bluetooth is being enabled 182226307d8c2952c42855005d7c0107b42b066bc9aMartijn Coenen mState = STATE_TURNING_ON; 183226307d8c2952c42855005d7c0107b42b066bc9aMartijn Coenen } else { 184d82d9db81f30ccecd29a0531e6db9b49c9c2cd95Martijn Coenen toast(mContext.getString(R.string.failed_to_enable_bt)); 185226307d8c2952c42855005d7c0107b42b066bc9aMartijn Coenen complete(false); 186226307d8c2952c42855005d7c0107b42b066bc9aMartijn Coenen } 187a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly break; 188a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly } 189a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly // fall-through 190a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly case STATE_TURNING_ON: 191a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly if (mDevice.getBondState() != BluetoothDevice.BOND_BONDED) { 192a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly startBonding(); 193a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly break; 194a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly } 195a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly // fall-through 196a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly case STATE_BONDING: 197a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly // Bluetooth Profile service will correctly serialize 198a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly // HFP then A2DP connect 199a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly mState = STATE_CONNECTING; 200a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly if (mHeadset.getConnectionState(mDevice) != BluetoothProfile.STATE_CONNECTED) { 201a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly mHfpResult = RESULT_PENDING; 202a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly mHeadset.connect(mDevice); 203a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly } else { 204a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly mHfpResult = RESULT_CONNECTED; 205a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly } 206a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly if (mA2dp.getConnectionState(mDevice) != BluetoothProfile.STATE_CONNECTED) { 207a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly mA2dpResult = RESULT_PENDING; 208a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly mA2dp.connect(mDevice); 209a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly } else { 210a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly mA2dpResult = RESULT_CONNECTED; 211a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly } 212a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly if (mA2dpResult == RESULT_PENDING || mHfpResult == RESULT_PENDING) { 213d82d9db81f30ccecd29a0531e6db9b49c9c2cd95Martijn Coenen toast(mContext.getString(R.string.connecting_headset) + " " + mName + "..."); 214a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly break; 215a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly } 216a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly // fall-through 217a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly case STATE_CONNECTING: 218a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly if (mA2dpResult == RESULT_PENDING || mHfpResult == RESULT_PENDING) { 219a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly // another connection type still pending 220a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly break; 221a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly } 222a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly if (mA2dpResult == RESULT_CONNECTED || mHfpResult == RESULT_CONNECTED) { 223a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly // we'll take either as success 224d82d9db81f30ccecd29a0531e6db9b49c9c2cd95Martijn Coenen toast(mContext.getString(R.string.connected_headset) + " " + mName); 225a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly if (mA2dpResult == RESULT_CONNECTED) startTheMusic(); 226226307d8c2952c42855005d7c0107b42b066bc9aMartijn Coenen complete(true); 227a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly } else { 228d82d9db81f30ccecd29a0531e6db9b49c9c2cd95Martijn Coenen toast (mContext.getString(R.string.connect_headset_failed) + " " + mName); 229226307d8c2952c42855005d7c0107b42b066bc9aMartijn Coenen complete(false); 230a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly } 231a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly break; 232a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly } 233a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly } 234a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly 23549913d6a0ed709ff6edde40bb040605997b561f3Nick Pelly void startBonding() { 236a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly mState = STATE_BONDING; 237d82d9db81f30ccecd29a0531e6db9b49c9c2cd95Martijn Coenen toast(mContext.getString(R.string.pairing_headset) + " " + mName + "..."); 238a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly if (!mDevice.createBond()) { 239d82d9db81f30ccecd29a0531e6db9b49c9c2cd95Martijn Coenen toast(mContext.getString(R.string.pairing_headset_failed) + " " + mName); 240226307d8c2952c42855005d7c0107b42b066bc9aMartijn Coenen complete(false); 241a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly } 242a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly } 243a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly 24449913d6a0ed709ff6edde40bb040605997b561f3Nick Pelly void handleIntent(Intent intent) { 245a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly String action = intent.getAction(); 246a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly if (BluetoothAdapter.ACTION_STATE_CHANGED.equals(action) && mState == STATE_TURNING_ON) { 247a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly int state = intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, BluetoothAdapter.ERROR); 248a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly if (state == BluetoothAdapter.STATE_ON) { 249a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly nextStepConnect(); 250a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly } else if (state == BluetoothAdapter.STATE_OFF) { 251d82d9db81f30ccecd29a0531e6db9b49c9c2cd95Martijn Coenen toast(mContext.getString(R.string.failed_to_enable_bt)); 252226307d8c2952c42855005d7c0107b42b066bc9aMartijn Coenen complete(false); 253a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly } 254a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly return; 255a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly } 256a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly 257a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly // Everything else requires the device to match... 258a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE); 259a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly if (!mDevice.equals(device)) return; 260a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly 261a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly if (BluetoothDevice.ACTION_BOND_STATE_CHANGED.equals(action) && mState == STATE_BONDING) { 262a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly int bond = intent.getIntExtra(BluetoothDevice.EXTRA_BOND_STATE, 263a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly BluetoothAdapter.ERROR); 264a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly if (bond == BluetoothDevice.BOND_BONDED) { 265a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly nextStepConnect(); 266a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly } else if (bond == BluetoothDevice.BOND_NONE) { 267d82d9db81f30ccecd29a0531e6db9b49c9c2cd95Martijn Coenen toast(mContext.getString(R.string.pairing_headset_failed) + " " + mName); 268226307d8c2952c42855005d7c0107b42b066bc9aMartijn Coenen complete(false); 269a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly } 270a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly } else if (BluetoothHeadset.ACTION_CONNECTION_STATE_CHANGED.equals(action) && 271a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly (mState == STATE_CONNECTING || mState == STATE_DISCONNECTING)) { 272a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly int state = intent.getIntExtra(BluetoothProfile.EXTRA_STATE, BluetoothAdapter.ERROR); 273a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly if (state == BluetoothProfile.STATE_CONNECTED) { 274a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly mHfpResult = RESULT_CONNECTED; 275a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly nextStep(); 276a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly } else if (state == BluetoothProfile.STATE_DISCONNECTED) { 277a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly mHfpResult = RESULT_DISCONNECTED; 278a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly nextStep(); 279a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly } 280a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly } else if (BluetoothA2dp.ACTION_CONNECTION_STATE_CHANGED.equals(action) && 281a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly (mState == STATE_CONNECTING || mState == STATE_DISCONNECTING)) { 282a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly int state = intent.getIntExtra(BluetoothProfile.EXTRA_STATE, BluetoothAdapter.ERROR); 283a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly if (state == BluetoothProfile.STATE_CONNECTED) { 284a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly mA2dpResult = RESULT_CONNECTED; 285a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly nextStep(); 286a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly } else if (state == BluetoothProfile.STATE_DISCONNECTED) { 287a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly mA2dpResult = RESULT_DISCONNECTED; 288a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly nextStep(); 289a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly } 290a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly } 291a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly } 292a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly 293226307d8c2952c42855005d7c0107b42b066bc9aMartijn Coenen void complete(boolean connected) { 294a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly if (DBG) Log.d(TAG, "complete()"); 295a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly mState = STATE_COMPLETE; 296a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly mContext.unregisterReceiver(mReceiver); 297a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly mHandler.removeMessages(MSG_TIMEOUT); 298226307d8c2952c42855005d7c0107b42b066bc9aMartijn Coenen mCallback.onBluetoothHeadsetHandoverComplete(connected); 299a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly } 300a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly 301a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly void toast(CharSequence text) { 30249913d6a0ed709ff6edde40bb040605997b561f3Nick Pelly Toast.makeText(mContext, text, Toast.LENGTH_SHORT).show(); 30349913d6a0ed709ff6edde40bb040605997b561f3Nick Pelly } 30449913d6a0ed709ff6edde40bb040605997b561f3Nick Pelly 30549913d6a0ed709ff6edde40bb040605997b561f3Nick Pelly void startTheMusic() { 30649913d6a0ed709ff6edde40bb040605997b561f3Nick Pelly Intent intent = new Intent(Intent.ACTION_MEDIA_BUTTON); 30749913d6a0ed709ff6edde40bb040605997b561f3Nick Pelly intent.putExtra(Intent.EXTRA_KEY_EVENT, new KeyEvent(KeyEvent.ACTION_DOWN, 30849913d6a0ed709ff6edde40bb040605997b561f3Nick Pelly KeyEvent.KEYCODE_MEDIA_PLAY)); 30949913d6a0ed709ff6edde40bb040605997b561f3Nick Pelly mContext.sendOrderedBroadcast(intent, null); 31049913d6a0ed709ff6edde40bb040605997b561f3Nick Pelly intent.putExtra(Intent.EXTRA_KEY_EVENT, new KeyEvent(KeyEvent.ACTION_UP, 31149913d6a0ed709ff6edde40bb040605997b561f3Nick Pelly KeyEvent.KEYCODE_MEDIA_PLAY)); 31249913d6a0ed709ff6edde40bb040605997b561f3Nick Pelly mContext.sendOrderedBroadcast(intent, null); 313a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly } 314a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly 315a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly final Handler mHandler = new Handler() { 316a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly @Override 317a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly public void handleMessage(Message msg) { 318a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly switch (msg.what) { 319a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly case MSG_TIMEOUT: 32049913d6a0ed709ff6edde40bb040605997b561f3Nick Pelly if (mState == STATE_COMPLETE) return; 32149913d6a0ed709ff6edde40bb040605997b561f3Nick Pelly Log.i(TAG, "Timeout completing BT handover"); 322226307d8c2952c42855005d7c0107b42b066bc9aMartijn Coenen complete(false); 323a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly break; 324a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly } 325a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly } 326a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly }; 327a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly 32849913d6a0ed709ff6edde40bb040605997b561f3Nick Pelly final BroadcastReceiver mReceiver = new BroadcastReceiver() { 32949913d6a0ed709ff6edde40bb040605997b561f3Nick Pelly @Override 33049913d6a0ed709ff6edde40bb040605997b561f3Nick Pelly public void onReceive(Context context, Intent intent) { 33149913d6a0ed709ff6edde40bb040605997b561f3Nick Pelly handleIntent(intent); 33249913d6a0ed709ff6edde40bb040605997b561f3Nick Pelly } 33349913d6a0ed709ff6edde40bb040605997b561f3Nick Pelly }; 33449913d6a0ed709ff6edde40bb040605997b561f3Nick Pelly 33549913d6a0ed709ff6edde40bb040605997b561f3Nick Pelly static void checkMainThread() { 33649913d6a0ed709ff6edde40bb040605997b561f3Nick Pelly if (Looper.myLooper() != Looper.getMainLooper()) { 33749913d6a0ed709ff6edde40bb040605997b561f3Nick Pelly throw new IllegalThreadStateException("must be called on main thread"); 33849913d6a0ed709ff6edde40bb040605997b561f3Nick Pelly } 339a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly } 340a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly} 341