BluetoothPeripheralHandover.java revision 226307d8c2952c42855005d7c0107b42b066bc9a
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; 36226307d8c2952c42855005d7c0107b42b066bc9aMartijn Coenen 37a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly/** 38a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly * Connects / Disconnects from a Bluetooth headset (or any device that 39a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly * might implement BT HSP, HFP or A2DP sink) when touched with NFC. 40a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly * 41a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly * This object is created on an NFC interaction, and determines what 42a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly * sequence of Bluetooth actions to take, and executes them. It is not 43a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly * designed to be re-used after the sequence has completed or timed out. 44a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly * Subsequent NFC interactions should use new objects. 45a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly * 46226307d8c2952c42855005d7c0107b42b066bc9aMartijn Coenen * TODO: prevent auto-connecting to other devices and other incoming a2dp/hsp 47226307d8c2952c42855005d7c0107b42b066bc9aMartijn Coenen * connects. 48a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly * TODO: il8n / UI review 49a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly */ 50a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pellypublic class BluetoothHeadsetHandover { 51a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly static final String TAG = HandoverManager.TAG; 52a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly static final boolean DBG = HandoverManager.DBG; 53a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly 54a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly static final int TIMEOUT_MS = 20000; 55a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly 56a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly static final int STATE_INIT = 0; 57a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly static final int STATE_TURNING_ON = 1; 58a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly static final int STATE_BONDING = 2; 59a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly static final int STATE_CONNECTING = 3; 60a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly static final int STATE_DISCONNECTING = 4; 61a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly static final int STATE_COMPLETE = 5; 62a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly 63a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly static final int RESULT_PENDING = 0; 64a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly static final int RESULT_CONNECTED = 1; 65a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly static final int RESULT_DISCONNECTED = 2; 66a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly 67a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly static final int ACTION_DISCONNECT = 1; 68a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly static final int ACTION_CONNECT = 2; 69a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly 7049913d6a0ed709ff6edde40bb040605997b561f3Nick Pelly static final int MSG_TIMEOUT = 1; 71a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly 72a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly final Context mContext; 73a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly final BluetoothDevice mDevice; 74a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly final String mName; 75226307d8c2952c42855005d7c0107b42b066bc9aMartijn Coenen final HandoverPowerManager mHandoverPowerManager; 76a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly final BluetoothA2dp mA2dp; 77a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly final BluetoothHeadset mHeadset; 78a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly final Callback mCallback; 79a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly 8049913d6a0ed709ff6edde40bb040605997b561f3Nick Pelly // only used on main thread 81a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly int mAction; 82a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly int mState; 83a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly int mHfpResult; // used only in STATE_CONNECTING and STATE_DISCONNETING 84a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly int mA2dpResult; // used only in STATE_CONNECTING and STATE_DISCONNETING 85a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly 86a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly public interface Callback { 87226307d8c2952c42855005d7c0107b42b066bc9aMartijn Coenen public void onBluetoothHeadsetHandoverComplete(boolean connected); 88a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly } 89a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly 90a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly public BluetoothHeadsetHandover(Context context, BluetoothDevice device, String name, 91226307d8c2952c42855005d7c0107b42b066bc9aMartijn Coenen HandoverPowerManager powerManager, BluetoothA2dp a2dp, BluetoothHeadset headset, 92a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly Callback callback) { 9349913d6a0ed709ff6edde40bb040605997b561f3Nick Pelly checkMainThread(); // mHandler must get get constructed on Main Thread for toasts to work 94a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly mContext = context; 95a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly mDevice = device; 96a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly mName = name; 97226307d8c2952c42855005d7c0107b42b066bc9aMartijn Coenen mHandoverPowerManager = powerManager; 98a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly mA2dp = a2dp; 99a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly mHeadset = headset; 100a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly mCallback = callback; 101a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly mState = STATE_INIT; 102a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly } 103a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly 104a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly /** 105a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly * Main entry point. This method is usually called after construction, 10649913d6a0ed709ff6edde40bb040605997b561f3Nick Pelly * to begin the BT sequence. Must be called on Main thread. 107a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly */ 10849913d6a0ed709ff6edde40bb040605997b561f3Nick Pelly public void start() { 10949913d6a0ed709ff6edde40bb040605997b561f3Nick Pelly checkMainThread(); 11049913d6a0ed709ff6edde40bb040605997b561f3Nick Pelly if (mState != STATE_INIT) return; 11149913d6a0ed709ff6edde40bb040605997b561f3Nick Pelly 112a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly IntentFilter filter = new IntentFilter(); 113a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly filter.addAction(BluetoothAdapter.ACTION_STATE_CHANGED); 114a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly filter.addAction(BluetoothDevice.ACTION_BOND_STATE_CHANGED); 115a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly filter.addAction(BluetoothA2dp.ACTION_CONNECTION_STATE_CHANGED); 116a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly filter.addAction(BluetoothHeadset.ACTION_CONNECTION_STATE_CHANGED); 117a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly mContext.registerReceiver(mReceiver, filter); 118a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly 119a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly if (mA2dp.getConnectedDevices().contains(mDevice) || 120a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly mHeadset.getConnectedDevices().contains(mDevice)) { 121a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly Log.i(TAG, "ACTION_DISCONNECT addr=" + mDevice + " name=" + mName); 122a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly mAction = ACTION_DISCONNECT; 123a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly } else { 124a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly Log.i(TAG, "ACTION_CONNECT addr=" + mDevice + " name=" + mName); 125a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly mAction = ACTION_CONNECT; 126a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly } 127a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly mHandler.sendMessageDelayed(mHandler.obtainMessage(MSG_TIMEOUT), TIMEOUT_MS); 128a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly nextStep(); 129a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly } 130a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly 131a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly /** 132a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly * Called to execute next step in state machine 133a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly */ 13449913d6a0ed709ff6edde40bb040605997b561f3Nick Pelly void nextStep() { 135a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly if (mAction == ACTION_CONNECT) { 136a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly nextStepConnect(); 137a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly } else { 138a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly nextStepDisconnect(); 139a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly } 140a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly } 141a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly 14249913d6a0ed709ff6edde40bb040605997b561f3Nick Pelly void nextStepDisconnect() { 143a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly switch (mState) { 144a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly case STATE_INIT: 145a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly mState = STATE_DISCONNECTING; 146a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly if (mHeadset.getConnectionState(mDevice) != BluetoothProfile.STATE_DISCONNECTED) { 147a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly mHfpResult = RESULT_PENDING; 148a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly mHeadset.disconnect(mDevice); 149a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly } else { 150a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly mHfpResult = RESULT_DISCONNECTED; 151a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly } 152a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly if (mA2dp.getConnectionState(mDevice) != BluetoothProfile.STATE_DISCONNECTED) { 153a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly mA2dpResult = RESULT_PENDING; 154a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly mA2dp.disconnect(mDevice); 155a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly } else { 156a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly mA2dpResult = RESULT_DISCONNECTED; 157a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly } 158a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly if (mA2dpResult == RESULT_PENDING || mHfpResult == RESULT_PENDING) { 159a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly toast("Disconnecting " + 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) { 169a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly toast("Disconnected " + 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 { 184226307d8c2952c42855005d7c0107b42b066bc9aMartijn Coenen toast("Failed to enable Bluetooth"); 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) { 213a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly toast("Connecting " + 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 224a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly toast("Connected " + mName); 225a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly if (mA2dpResult == RESULT_CONNECTED) startTheMusic(); 226226307d8c2952c42855005d7c0107b42b066bc9aMartijn Coenen complete(true); 227a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly } else { 228a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly toast ("Failed to connect " + 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; 237a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly toast("Pairing " + mName + "..."); 238a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly if (!mDevice.createBond()) { 239a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly toast("Failed to pair " + 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) { 251a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly toast("Failed to enable Bluetooth"); 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) { 267a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly toast("Failed to pair " + 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