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; 237aaafa209be6c1b01081cfb5ed1e9530082c7d8cAndres Moralesimport android.bluetooth.BluetoothInputDevice; 24a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pellyimport android.bluetooth.BluetoothProfile; 25ca75f7282125255caee81f4bcd8f765d709acc38Jakub Pawlowskiimport android.bluetooth.OobData; 26a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pellyimport android.content.BroadcastReceiver; 27cf76dfbf36ab621b9372527ddc8ca605e00d7784Andres Moralesimport android.content.ContentResolver; 28a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pellyimport android.content.Context; 29a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pellyimport android.content.Intent; 30a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pellyimport android.content.IntentFilter; 315f46ed88ca42cbe7a8930421775d4b8d69f4aaf6RoboErikimport android.media.session.MediaSessionLegacyHelper; 32a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pellyimport android.os.Handler; 33a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pellyimport android.os.Looper; 34a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pellyimport android.os.Message; 357aaafa209be6c1b01081cfb5ed1e9530082c7d8cAndres Moralesimport android.os.ParcelUuid; 36cf76dfbf36ab621b9372527ddc8ca605e00d7784Andres Moralesimport android.provider.Settings; 37a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pellyimport android.util.Log; 38a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pellyimport android.view.KeyEvent; 39a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pellyimport android.widget.Toast; 40a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly 41d82d9db81f30ccecd29a0531e6db9b49c9c2cd95Martijn Coenenimport com.android.nfc.R; 42226307d8c2952c42855005d7c0107b42b066bc9aMartijn Coenen 43a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly/** 44a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly * Connects / Disconnects from a Bluetooth headset (or any device that 457aaafa209be6c1b01081cfb5ed1e9530082c7d8cAndres Morales * might implement BT HSP, HFP, A2DP, or HOGP sink) when touched with NFC. 46a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly * 47a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly * This object is created on an NFC interaction, and determines what 48a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly * sequence of Bluetooth actions to take, and executes them. It is not 49a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly * designed to be re-used after the sequence has completed or timed out. 50a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly * Subsequent NFC interactions should use new objects. 51a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly * 52a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly */ 537aaafa209be6c1b01081cfb5ed1e9530082c7d8cAndres Moralespublic class BluetoothPeripheralHandover implements BluetoothProfile.ServiceListener { 547aaafa209be6c1b01081cfb5ed1e9530082c7d8cAndres Morales static final String TAG = "BluetoothPeripheralHandover"; 5524fc77fd89872c91bfd0c331645f0513ea05d99dAndres Morales static final boolean DBG = false; 56a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly 5778705adb33530d58f6b25bd3976ef36df196af55Martijn Coenen static final String ACTION_ALLOW_CONNECT = "com.android.nfc.handover.action.ALLOW_CONNECT"; 5878705adb33530d58f6b25bd3976ef36df196af55Martijn Coenen static final String ACTION_DENY_CONNECT = "com.android.nfc.handover.action.DENY_CONNECT"; 5978705adb33530d58f6b25bd3976ef36df196af55Martijn Coenen 60a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly static final int TIMEOUT_MS = 20000; 61a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly 62a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly static final int STATE_INIT = 0; 63be1939b4b6003ac7a65fcb95a3912f5e1ce8e75fThe Android Open Source Project static final int STATE_WAITING_FOR_PROXIES = 1; 64be1939b4b6003ac7a65fcb95a3912f5e1ce8e75fThe Android Open Source Project static final int STATE_INIT_COMPLETE = 2; 65be1939b4b6003ac7a65fcb95a3912f5e1ce8e75fThe Android Open Source Project static final int STATE_WAITING_FOR_BOND_CONFIRMATION = 3; 66be1939b4b6003ac7a65fcb95a3912f5e1ce8e75fThe Android Open Source Project static final int STATE_BONDING = 4; 67be1939b4b6003ac7a65fcb95a3912f5e1ce8e75fThe Android Open Source Project static final int STATE_CONNECTING = 5; 68be1939b4b6003ac7a65fcb95a3912f5e1ce8e75fThe Android Open Source Project static final int STATE_DISCONNECTING = 6; 69be1939b4b6003ac7a65fcb95a3912f5e1ce8e75fThe Android Open Source Project static final int STATE_COMPLETE = 7; 70a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly 71a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly static final int RESULT_PENDING = 0; 72a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly static final int RESULT_CONNECTED = 1; 73a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly static final int RESULT_DISCONNECTED = 2; 74a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly 75525c260303268a83da4c3413b953d13c9084e834The Android Open Source Project static final int ACTION_INIT = 0; 76a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly static final int ACTION_DISCONNECT = 1; 77a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly static final int ACTION_CONNECT = 2; 78a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly 7949913d6a0ed709ff6edde40bb040605997b561f3Nick Pelly static final int MSG_TIMEOUT = 1; 80525c260303268a83da4c3413b953d13c9084e834The Android Open Source Project static final int MSG_NEXT_STEP = 2; 81a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly 82a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly final Context mContext; 83a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly final BluetoothDevice mDevice; 84a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly final String mName; 85a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly final Callback mCallback; 86525c260303268a83da4c3413b953d13c9084e834The Android Open Source Project final BluetoothAdapter mBluetoothAdapter; 877aaafa209be6c1b01081cfb5ed1e9530082c7d8cAndres Morales final int mTransport; 88cf76dfbf36ab621b9372527ddc8ca605e00d7784Andres Morales final boolean mProvisioning; 89525c260303268a83da4c3413b953d13c9084e834The Android Open Source Project 90525c260303268a83da4c3413b953d13c9084e834The Android Open Source Project final Object mLock = new Object(); 91a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly 9249913d6a0ed709ff6edde40bb040605997b561f3Nick Pelly // only used on main thread 93a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly int mAction; 94a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly int mState; 95a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly int mHfpResult; // used only in STATE_CONNECTING and STATE_DISCONNETING 96a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly int mA2dpResult; // used only in STATE_CONNECTING and STATE_DISCONNETING 977aaafa209be6c1b01081cfb5ed1e9530082c7d8cAndres Morales int mHidResult; 98ca75f7282125255caee81f4bcd8f765d709acc38Jakub Pawlowski OobData mOobData; 99a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly 100525c260303268a83da4c3413b953d13c9084e834The Android Open Source Project // protected by mLock 101525c260303268a83da4c3413b953d13c9084e834The Android Open Source Project BluetoothA2dp mA2dp; 102525c260303268a83da4c3413b953d13c9084e834The Android Open Source Project BluetoothHeadset mHeadset; 1037aaafa209be6c1b01081cfb5ed1e9530082c7d8cAndres Morales BluetoothInputDevice mInput; 104525c260303268a83da4c3413b953d13c9084e834The Android Open Source Project 105a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly public interface Callback { 1067aaafa209be6c1b01081cfb5ed1e9530082c7d8cAndres Morales public void onBluetoothPeripheralHandoverComplete(boolean connected); 107a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly } 108a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly 1097aaafa209be6c1b01081cfb5ed1e9530082c7d8cAndres Morales public BluetoothPeripheralHandover(Context context, BluetoothDevice device, String name, 110ca75f7282125255caee81f4bcd8f765d709acc38Jakub Pawlowski int transport, OobData oobData, Callback callback) { 11149913d6a0ed709ff6edde40bb040605997b561f3Nick Pelly checkMainThread(); // mHandler must get get constructed on Main Thread for toasts to work 112a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly mContext = context; 113a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly mDevice = device; 114a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly mName = name; 1157aaafa209be6c1b01081cfb5ed1e9530082c7d8cAndres Morales mTransport = transport; 116ca75f7282125255caee81f4bcd8f765d709acc38Jakub Pawlowski mOobData = oobData; 117a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly mCallback = callback; 118525c260303268a83da4c3413b953d13c9084e834The Android Open Source Project mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter(); 119525c260303268a83da4c3413b953d13c9084e834The Android Open Source Project 120cf76dfbf36ab621b9372527ddc8ca605e00d7784Andres Morales ContentResolver contentResolver = mContext.getContentResolver(); 121cf76dfbf36ab621b9372527ddc8ca605e00d7784Andres Morales mProvisioning = Settings.Secure.getInt(contentResolver, 122cf76dfbf36ab621b9372527ddc8ca605e00d7784Andres Morales Settings.Global.DEVICE_PROVISIONED, 0) == 0; 123cf76dfbf36ab621b9372527ddc8ca605e00d7784Andres Morales 124a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly mState = STATE_INIT; 125a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly } 126a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly 127be1939b4b6003ac7a65fcb95a3912f5e1ce8e75fThe Android Open Source Project public boolean hasStarted() { 128be1939b4b6003ac7a65fcb95a3912f5e1ce8e75fThe Android Open Source Project return mState != STATE_INIT; 129be1939b4b6003ac7a65fcb95a3912f5e1ce8e75fThe Android Open Source Project } 130be1939b4b6003ac7a65fcb95a3912f5e1ce8e75fThe Android Open Source Project 131a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly /** 132a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly * Main entry point. This method is usually called after construction, 13349913d6a0ed709ff6edde40bb040605997b561f3Nick Pelly * to begin the BT sequence. Must be called on Main thread. 134a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly */ 13542c0f35783e2849de525cdbe743c95c197fdcf50Andres Morales public boolean start() { 13649913d6a0ed709ff6edde40bb040605997b561f3Nick Pelly checkMainThread(); 13742c0f35783e2849de525cdbe743c95c197fdcf50Andres Morales if (mState != STATE_INIT || mBluetoothAdapter == null 13842c0f35783e2849de525cdbe743c95c197fdcf50Andres Morales || (mProvisioning && mTransport != BluetoothDevice.TRANSPORT_LE)) { 13942c0f35783e2849de525cdbe743c95c197fdcf50Andres Morales return false; 14042c0f35783e2849de525cdbe743c95c197fdcf50Andres Morales } 141cf76dfbf36ab621b9372527ddc8ca605e00d7784Andres Morales 14249913d6a0ed709ff6edde40bb040605997b561f3Nick Pelly 143a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly IntentFilter filter = new IntentFilter(); 144a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly filter.addAction(BluetoothAdapter.ACTION_STATE_CHANGED); 145a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly filter.addAction(BluetoothDevice.ACTION_BOND_STATE_CHANGED); 146a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly filter.addAction(BluetoothA2dp.ACTION_CONNECTION_STATE_CHANGED); 147a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly filter.addAction(BluetoothHeadset.ACTION_CONNECTION_STATE_CHANGED); 1487aaafa209be6c1b01081cfb5ed1e9530082c7d8cAndres Morales filter.addAction(BluetoothInputDevice.ACTION_CONNECTION_STATE_CHANGED); 14978705adb33530d58f6b25bd3976ef36df196af55Martijn Coenen filter.addAction(ACTION_ALLOW_CONNECT); 15078705adb33530d58f6b25bd3976ef36df196af55Martijn Coenen filter.addAction(ACTION_DENY_CONNECT); 15178705adb33530d58f6b25bd3976ef36df196af55Martijn Coenen 152a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly mContext.registerReceiver(mReceiver, filter); 153a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly 154a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly mHandler.sendMessageDelayed(mHandler.obtainMessage(MSG_TIMEOUT), TIMEOUT_MS); 1557aaafa209be6c1b01081cfb5ed1e9530082c7d8cAndres Morales 156525c260303268a83da4c3413b953d13c9084e834The Android Open Source Project mAction = ACTION_INIT; 1577aaafa209be6c1b01081cfb5ed1e9530082c7d8cAndres Morales 158a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly nextStep(); 15942c0f35783e2849de525cdbe743c95c197fdcf50Andres Morales 16042c0f35783e2849de525cdbe743c95c197fdcf50Andres Morales return true; 161a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly } 162a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly 163a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly /** 164a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly * Called to execute next step in state machine 165a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly */ 16649913d6a0ed709ff6edde40bb040605997b561f3Nick Pelly void nextStep() { 167525c260303268a83da4c3413b953d13c9084e834The Android Open Source Project if (mAction == ACTION_INIT) { 168525c260303268a83da4c3413b953d13c9084e834The Android Open Source Project nextStepInit(); 169525c260303268a83da4c3413b953d13c9084e834The Android Open Source Project } else if (mAction == ACTION_CONNECT) { 170a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly nextStepConnect(); 171a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly } else { 172a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly nextStepDisconnect(); 173a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly } 174a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly } 175a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly 176525c260303268a83da4c3413b953d13c9084e834The Android Open Source Project /* 177525c260303268a83da4c3413b953d13c9084e834The Android Open Source Project * Enables bluetooth and gets the profile proxies 178525c260303268a83da4c3413b953d13c9084e834The Android Open Source Project */ 179525c260303268a83da4c3413b953d13c9084e834The Android Open Source Project void nextStepInit() { 180a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly switch (mState) { 181a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly case STATE_INIT: 1827aaafa209be6c1b01081cfb5ed1e9530082c7d8cAndres Morales if (mA2dp == null || mHeadset == null || mInput == null) { 183525c260303268a83da4c3413b953d13c9084e834The Android Open Source Project mState = STATE_WAITING_FOR_PROXIES; 184525c260303268a83da4c3413b953d13c9084e834The Android Open Source Project if (!getProfileProxys()) { 185525c260303268a83da4c3413b953d13c9084e834The Android Open Source Project complete(false); 186525c260303268a83da4c3413b953d13c9084e834The Android Open Source Project } 187a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly break; 188a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly } 189a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly // fall-through 190525c260303268a83da4c3413b953d13c9084e834The Android Open Source Project case STATE_WAITING_FOR_PROXIES: 191525c260303268a83da4c3413b953d13c9084e834The Android Open Source Project mState = STATE_INIT_COMPLETE; 192525c260303268a83da4c3413b953d13c9084e834The Android Open Source Project // Check connected devices and see if we need to disconnect 193525c260303268a83da4c3413b953d13c9084e834The Android Open Source Project synchronized(mLock) { 1947aaafa209be6c1b01081cfb5ed1e9530082c7d8cAndres Morales if (mTransport == BluetoothDevice.TRANSPORT_LE) { 1957aaafa209be6c1b01081cfb5ed1e9530082c7d8cAndres Morales if (mInput.getConnectedDevices().contains(mDevice)) { 1967aaafa209be6c1b01081cfb5ed1e9530082c7d8cAndres Morales Log.i(TAG, "ACTION_DISCONNECT addr=" + mDevice + " name=" + mName); 1977aaafa209be6c1b01081cfb5ed1e9530082c7d8cAndres Morales mAction = ACTION_DISCONNECT; 1987aaafa209be6c1b01081cfb5ed1e9530082c7d8cAndres Morales } else { 1997aaafa209be6c1b01081cfb5ed1e9530082c7d8cAndres Morales Log.i(TAG, "ACTION_CONNECT addr=" + mDevice + " name=" + mName); 2007aaafa209be6c1b01081cfb5ed1e9530082c7d8cAndres Morales mAction = ACTION_CONNECT; 2017aaafa209be6c1b01081cfb5ed1e9530082c7d8cAndres Morales } 202525c260303268a83da4c3413b953d13c9084e834The Android Open Source Project } else { 2037aaafa209be6c1b01081cfb5ed1e9530082c7d8cAndres Morales if (mA2dp.getConnectedDevices().contains(mDevice) || 2047aaafa209be6c1b01081cfb5ed1e9530082c7d8cAndres Morales mHeadset.getConnectedDevices().contains(mDevice)) { 2057aaafa209be6c1b01081cfb5ed1e9530082c7d8cAndres Morales Log.i(TAG, "ACTION_DISCONNECT addr=" + mDevice + " name=" + mName); 2067aaafa209be6c1b01081cfb5ed1e9530082c7d8cAndres Morales mAction = ACTION_DISCONNECT; 2077aaafa209be6c1b01081cfb5ed1e9530082c7d8cAndres Morales } else { 2087aaafa209be6c1b01081cfb5ed1e9530082c7d8cAndres Morales Log.i(TAG, "ACTION_CONNECT addr=" + mDevice + " name=" + mName); 2097aaafa209be6c1b01081cfb5ed1e9530082c7d8cAndres Morales mAction = ACTION_CONNECT; 2107aaafa209be6c1b01081cfb5ed1e9530082c7d8cAndres Morales } 211525c260303268a83da4c3413b953d13c9084e834The Android Open Source Project } 212525c260303268a83da4c3413b953d13c9084e834The Android Open Source Project } 213525c260303268a83da4c3413b953d13c9084e834The Android Open Source Project nextStep(); 214525c260303268a83da4c3413b953d13c9084e834The Android Open Source Project } 215525c260303268a83da4c3413b953d13c9084e834The Android Open Source Project 216525c260303268a83da4c3413b953d13c9084e834The Android Open Source Project } 217525c260303268a83da4c3413b953d13c9084e834The Android Open Source Project 218525c260303268a83da4c3413b953d13c9084e834The Android Open Source Project void nextStepDisconnect() { 219525c260303268a83da4c3413b953d13c9084e834The Android Open Source Project switch (mState) { 220525c260303268a83da4c3413b953d13c9084e834The Android Open Source Project case STATE_INIT_COMPLETE: 221525c260303268a83da4c3413b953d13c9084e834The Android Open Source Project mState = STATE_DISCONNECTING; 222525c260303268a83da4c3413b953d13c9084e834The Android Open Source Project synchronized (mLock) { 2237aaafa209be6c1b01081cfb5ed1e9530082c7d8cAndres Morales if (mTransport == BluetoothDevice.TRANSPORT_LE) { 2247aaafa209be6c1b01081cfb5ed1e9530082c7d8cAndres Morales if (mInput.getConnectionState(mDevice) 2257aaafa209be6c1b01081cfb5ed1e9530082c7d8cAndres Morales != BluetoothProfile.STATE_DISCONNECTED) { 2267aaafa209be6c1b01081cfb5ed1e9530082c7d8cAndres Morales mHidResult = RESULT_PENDING; 2277aaafa209be6c1b01081cfb5ed1e9530082c7d8cAndres Morales mInput.disconnect(mDevice); 228dceae917c7aeb73dfce10ab5e2e9143fb988c4a8Andres Morales toast(getToastString(R.string.disconnecting_peripheral)); 2297aaafa209be6c1b01081cfb5ed1e9530082c7d8cAndres Morales break; 2307aaafa209be6c1b01081cfb5ed1e9530082c7d8cAndres Morales } else { 2317aaafa209be6c1b01081cfb5ed1e9530082c7d8cAndres Morales mHidResult = RESULT_DISCONNECTED; 2327aaafa209be6c1b01081cfb5ed1e9530082c7d8cAndres Morales } 233525c260303268a83da4c3413b953d13c9084e834The Android Open Source Project } else { 2347aaafa209be6c1b01081cfb5ed1e9530082c7d8cAndres Morales if (mHeadset.getConnectionState(mDevice) 2357aaafa209be6c1b01081cfb5ed1e9530082c7d8cAndres Morales != BluetoothProfile.STATE_DISCONNECTED) { 2367aaafa209be6c1b01081cfb5ed1e9530082c7d8cAndres Morales mHfpResult = RESULT_PENDING; 2377aaafa209be6c1b01081cfb5ed1e9530082c7d8cAndres Morales mHeadset.disconnect(mDevice); 2387aaafa209be6c1b01081cfb5ed1e9530082c7d8cAndres Morales } else { 2397aaafa209be6c1b01081cfb5ed1e9530082c7d8cAndres Morales mHfpResult = RESULT_DISCONNECTED; 2407aaafa209be6c1b01081cfb5ed1e9530082c7d8cAndres Morales } 2417aaafa209be6c1b01081cfb5ed1e9530082c7d8cAndres Morales if (mA2dp.getConnectionState(mDevice) 2427aaafa209be6c1b01081cfb5ed1e9530082c7d8cAndres Morales != BluetoothProfile.STATE_DISCONNECTED) { 2437aaafa209be6c1b01081cfb5ed1e9530082c7d8cAndres Morales mA2dpResult = RESULT_PENDING; 2447aaafa209be6c1b01081cfb5ed1e9530082c7d8cAndres Morales mA2dp.disconnect(mDevice); 2457aaafa209be6c1b01081cfb5ed1e9530082c7d8cAndres Morales } else { 2467aaafa209be6c1b01081cfb5ed1e9530082c7d8cAndres Morales mA2dpResult = RESULT_DISCONNECTED; 2477aaafa209be6c1b01081cfb5ed1e9530082c7d8cAndres Morales } 2487aaafa209be6c1b01081cfb5ed1e9530082c7d8cAndres Morales if (mA2dpResult == RESULT_PENDING || mHfpResult == RESULT_PENDING) { 249dceae917c7aeb73dfce10ab5e2e9143fb988c4a8Andres Morales toast(getToastString(R.string.disconnecting_peripheral)); 2507aaafa209be6c1b01081cfb5ed1e9530082c7d8cAndres Morales break; 2517aaafa209be6c1b01081cfb5ed1e9530082c7d8cAndres Morales } 252525c260303268a83da4c3413b953d13c9084e834The Android Open Source Project } 2537aaafa209be6c1b01081cfb5ed1e9530082c7d8cAndres Morales } 2547aaafa209be6c1b01081cfb5ed1e9530082c7d8cAndres Morales // fall-through 2557aaafa209be6c1b01081cfb5ed1e9530082c7d8cAndres Morales case STATE_DISCONNECTING: 2567aaafa209be6c1b01081cfb5ed1e9530082c7d8cAndres Morales if (mTransport == BluetoothDevice.TRANSPORT_LE) { 2577aaafa209be6c1b01081cfb5ed1e9530082c7d8cAndres Morales if (mHidResult == RESULT_DISCONNECTED) { 258dceae917c7aeb73dfce10ab5e2e9143fb988c4a8Andres Morales toast(getToastString(R.string.disconnected_peripheral)); 2597aaafa209be6c1b01081cfb5ed1e9530082c7d8cAndres Morales complete(false); 260525c260303268a83da4c3413b953d13c9084e834The Android Open Source Project } 2617aaafa209be6c1b01081cfb5ed1e9530082c7d8cAndres Morales 2627aaafa209be6c1b01081cfb5ed1e9530082c7d8cAndres Morales break; 2637aaafa209be6c1b01081cfb5ed1e9530082c7d8cAndres Morales } else { 264525c260303268a83da4c3413b953d13c9084e834The Android Open Source Project if (mA2dpResult == RESULT_PENDING || mHfpResult == RESULT_PENDING) { 2657aaafa209be6c1b01081cfb5ed1e9530082c7d8cAndres Morales // still disconnecting 266525c260303268a83da4c3413b953d13c9084e834The Android Open Source Project break; 267525c260303268a83da4c3413b953d13c9084e834The Android Open Source Project } 2687aaafa209be6c1b01081cfb5ed1e9530082c7d8cAndres Morales if (mA2dpResult == RESULT_DISCONNECTED && mHfpResult == RESULT_DISCONNECTED) { 269dceae917c7aeb73dfce10ab5e2e9143fb988c4a8Andres Morales toast(getToastString(R.string.disconnected_peripheral)); 2707aaafa209be6c1b01081cfb5ed1e9530082c7d8cAndres Morales } 2717aaafa209be6c1b01081cfb5ed1e9530082c7d8cAndres Morales complete(false); 272a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly break; 273a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly } 2747aaafa209be6c1b01081cfb5ed1e9530082c7d8cAndres Morales 275a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly } 276525c260303268a83da4c3413b953d13c9084e834The Android Open Source Project 277525c260303268a83da4c3413b953d13c9084e834The Android Open Source Project } 278525c260303268a83da4c3413b953d13c9084e834The Android Open Source Project 279dceae917c7aeb73dfce10ab5e2e9143fb988c4a8Andres Morales private String getToastString(int resid) { 280dceae917c7aeb73dfce10ab5e2e9143fb988c4a8Andres Morales return mContext.getString(resid, mName != null ? mName : R.string.device); 281dceae917c7aeb73dfce10ab5e2e9143fb988c4a8Andres Morales } 282dceae917c7aeb73dfce10ab5e2e9143fb988c4a8Andres Morales 283525c260303268a83da4c3413b953d13c9084e834The Android Open Source Project boolean getProfileProxys() { 284525c260303268a83da4c3413b953d13c9084e834The Android Open Source Project 2857aaafa209be6c1b01081cfb5ed1e9530082c7d8cAndres Morales if (mTransport == BluetoothDevice.TRANSPORT_LE) { 2867aaafa209be6c1b01081cfb5ed1e9530082c7d8cAndres Morales if (!mBluetoothAdapter.getProfileProxy(mContext, this, BluetoothProfile.INPUT_DEVICE)) 2877aaafa209be6c1b01081cfb5ed1e9530082c7d8cAndres Morales return false; 2887aaafa209be6c1b01081cfb5ed1e9530082c7d8cAndres Morales } else { 2897aaafa209be6c1b01081cfb5ed1e9530082c7d8cAndres Morales if(!mBluetoothAdapter.getProfileProxy(mContext, this, BluetoothProfile.HEADSET)) 2907aaafa209be6c1b01081cfb5ed1e9530082c7d8cAndres Morales return false; 2917aaafa209be6c1b01081cfb5ed1e9530082c7d8cAndres Morales 2927aaafa209be6c1b01081cfb5ed1e9530082c7d8cAndres Morales if(!mBluetoothAdapter.getProfileProxy(mContext, this, BluetoothProfile.A2DP)) 2937aaafa209be6c1b01081cfb5ed1e9530082c7d8cAndres Morales return false; 2947aaafa209be6c1b01081cfb5ed1e9530082c7d8cAndres Morales } 295525c260303268a83da4c3413b953d13c9084e834The Android Open Source Project 296525c260303268a83da4c3413b953d13c9084e834The Android Open Source Project return true; 297a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly } 298a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly 29949913d6a0ed709ff6edde40bb040605997b561f3Nick Pelly void nextStepConnect() { 300a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly switch (mState) { 301525c260303268a83da4c3413b953d13c9084e834The Android Open Source Project case STATE_INIT_COMPLETE: 302c6204dc79ac89d30e20cc39f139ee10dd304de7fAndres Morales 303a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly if (mDevice.getBondState() != BluetoothDevice.BOND_BONDED) { 30478705adb33530d58f6b25bd3976ef36df196af55Martijn Coenen requestPairConfirmation(); 30578705adb33530d58f6b25bd3976ef36df196af55Martijn Coenen mState = STATE_WAITING_FOR_BOND_CONFIRMATION; 30678705adb33530d58f6b25bd3976ef36df196af55Martijn Coenen break; 30778705adb33530d58f6b25bd3976ef36df196af55Martijn Coenen } 30842c0f35783e2849de525cdbe743c95c197fdcf50Andres Morales 30942c0f35783e2849de525cdbe743c95c197fdcf50Andres Morales if (mTransport == BluetoothDevice.TRANSPORT_LE) { 31042c0f35783e2849de525cdbe743c95c197fdcf50Andres Morales if (mDevice.getBondState() != BluetoothDevice.BOND_NONE) { 31142c0f35783e2849de525cdbe743c95c197fdcf50Andres Morales mDevice.removeBond(); 31242c0f35783e2849de525cdbe743c95c197fdcf50Andres Morales requestPairConfirmation(); 31342c0f35783e2849de525cdbe743c95c197fdcf50Andres Morales mState = STATE_WAITING_FOR_BOND_CONFIRMATION; 31442c0f35783e2849de525cdbe743c95c197fdcf50Andres Morales break; 31542c0f35783e2849de525cdbe743c95c197fdcf50Andres Morales } 31642c0f35783e2849de525cdbe743c95c197fdcf50Andres Morales } 31778705adb33530d58f6b25bd3976ef36df196af55Martijn Coenen // fall-through 31878705adb33530d58f6b25bd3976ef36df196af55Martijn Coenen case STATE_WAITING_FOR_BOND_CONFIRMATION: 31978705adb33530d58f6b25bd3976ef36df196af55Martijn Coenen if (mDevice.getBondState() != BluetoothDevice.BOND_BONDED) { 320a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly startBonding(); 321a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly break; 322a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly } 323a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly // fall-through 324a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly case STATE_BONDING: 325a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly // Bluetooth Profile service will correctly serialize 326a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly // HFP then A2DP connect 327a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly mState = STATE_CONNECTING; 328525c260303268a83da4c3413b953d13c9084e834The Android Open Source Project synchronized (mLock) { 3297aaafa209be6c1b01081cfb5ed1e9530082c7d8cAndres Morales if (mTransport == BluetoothDevice.TRANSPORT_LE) { 3307aaafa209be6c1b01081cfb5ed1e9530082c7d8cAndres Morales if (mInput.getConnectionState(mDevice) 3317aaafa209be6c1b01081cfb5ed1e9530082c7d8cAndres Morales != BluetoothProfile.STATE_CONNECTED) { 3327aaafa209be6c1b01081cfb5ed1e9530082c7d8cAndres Morales mHidResult = RESULT_PENDING; 3337aaafa209be6c1b01081cfb5ed1e9530082c7d8cAndres Morales mInput.connect(mDevice); 334dceae917c7aeb73dfce10ab5e2e9143fb988c4a8Andres Morales toast(getToastString(R.string.connecting_peripheral)); 3357aaafa209be6c1b01081cfb5ed1e9530082c7d8cAndres Morales break; 3367aaafa209be6c1b01081cfb5ed1e9530082c7d8cAndres Morales } else { 3377aaafa209be6c1b01081cfb5ed1e9530082c7d8cAndres Morales mHidResult = RESULT_CONNECTED; 3387aaafa209be6c1b01081cfb5ed1e9530082c7d8cAndres Morales } 339525c260303268a83da4c3413b953d13c9084e834The Android Open Source Project } else { 3407aaafa209be6c1b01081cfb5ed1e9530082c7d8cAndres Morales if (mHeadset.getConnectionState(mDevice) != 3417aaafa209be6c1b01081cfb5ed1e9530082c7d8cAndres Morales BluetoothProfile.STATE_CONNECTED) { 3427aaafa209be6c1b01081cfb5ed1e9530082c7d8cAndres Morales mHfpResult = RESULT_PENDING; 3437aaafa209be6c1b01081cfb5ed1e9530082c7d8cAndres Morales mHeadset.connect(mDevice); 3447aaafa209be6c1b01081cfb5ed1e9530082c7d8cAndres Morales } else { 3457aaafa209be6c1b01081cfb5ed1e9530082c7d8cAndres Morales mHfpResult = RESULT_CONNECTED; 3467aaafa209be6c1b01081cfb5ed1e9530082c7d8cAndres Morales } 3477aaafa209be6c1b01081cfb5ed1e9530082c7d8cAndres Morales if (mA2dp.getConnectionState(mDevice) != BluetoothProfile.STATE_CONNECTED) { 3487aaafa209be6c1b01081cfb5ed1e9530082c7d8cAndres Morales mA2dpResult = RESULT_PENDING; 3497aaafa209be6c1b01081cfb5ed1e9530082c7d8cAndres Morales mA2dp.connect(mDevice); 3507aaafa209be6c1b01081cfb5ed1e9530082c7d8cAndres Morales } else { 3517aaafa209be6c1b01081cfb5ed1e9530082c7d8cAndres Morales mA2dpResult = RESULT_CONNECTED; 3527aaafa209be6c1b01081cfb5ed1e9530082c7d8cAndres Morales } 3537aaafa209be6c1b01081cfb5ed1e9530082c7d8cAndres Morales if (mA2dpResult == RESULT_PENDING || mHfpResult == RESULT_PENDING) { 354dceae917c7aeb73dfce10ab5e2e9143fb988c4a8Andres Morales toast(getToastString(R.string.connecting_peripheral)); 3557aaafa209be6c1b01081cfb5ed1e9530082c7d8cAndres Morales break; 3567aaafa209be6c1b01081cfb5ed1e9530082c7d8cAndres Morales } 357525c260303268a83da4c3413b953d13c9084e834The Android Open Source Project } 3587aaafa209be6c1b01081cfb5ed1e9530082c7d8cAndres Morales } 3597aaafa209be6c1b01081cfb5ed1e9530082c7d8cAndres Morales // fall-through 3607aaafa209be6c1b01081cfb5ed1e9530082c7d8cAndres Morales case STATE_CONNECTING: 3617aaafa209be6c1b01081cfb5ed1e9530082c7d8cAndres Morales if (mTransport == BluetoothDevice.TRANSPORT_LE) { 3627aaafa209be6c1b01081cfb5ed1e9530082c7d8cAndres Morales if (mHidResult == RESULT_PENDING) { 3637aaafa209be6c1b01081cfb5ed1e9530082c7d8cAndres Morales break; 3647aaafa209be6c1b01081cfb5ed1e9530082c7d8cAndres Morales } else if (mHidResult == RESULT_CONNECTED) { 365dceae917c7aeb73dfce10ab5e2e9143fb988c4a8Andres Morales toast(getToastString(R.string.connected_peripheral)); 366c6204dc79ac89d30e20cc39f139ee10dd304de7fAndres Morales mDevice.setAlias(mName); 3677aaafa209be6c1b01081cfb5ed1e9530082c7d8cAndres Morales complete(true); 368525c260303268a83da4c3413b953d13c9084e834The Android Open Source Project } else { 369dceae917c7aeb73dfce10ab5e2e9143fb988c4a8Andres Morales toast (getToastString(R.string.connect_peripheral_failed)); 3707aaafa209be6c1b01081cfb5ed1e9530082c7d8cAndres Morales complete(false); 371525c260303268a83da4c3413b953d13c9084e834The Android Open Source Project } 3727aaafa209be6c1b01081cfb5ed1e9530082c7d8cAndres Morales } else { 373525c260303268a83da4c3413b953d13c9084e834The Android Open Source Project if (mA2dpResult == RESULT_PENDING || mHfpResult == RESULT_PENDING) { 3747aaafa209be6c1b01081cfb5ed1e9530082c7d8cAndres Morales // another connection type still pending 375525c260303268a83da4c3413b953d13c9084e834The Android Open Source Project break; 376525c260303268a83da4c3413b953d13c9084e834The Android Open Source Project } 3777aaafa209be6c1b01081cfb5ed1e9530082c7d8cAndres Morales if (mA2dpResult == RESULT_CONNECTED || mHfpResult == RESULT_CONNECTED) { 3787aaafa209be6c1b01081cfb5ed1e9530082c7d8cAndres Morales // we'll take either as success 379dceae917c7aeb73dfce10ab5e2e9143fb988c4a8Andres Morales toast(getToastString(R.string.connected_peripheral)); 3807aaafa209be6c1b01081cfb5ed1e9530082c7d8cAndres Morales if (mA2dpResult == RESULT_CONNECTED) startTheMusic(); 381c6204dc79ac89d30e20cc39f139ee10dd304de7fAndres Morales mDevice.setAlias(mName); 3827aaafa209be6c1b01081cfb5ed1e9530082c7d8cAndres Morales complete(true); 3837aaafa209be6c1b01081cfb5ed1e9530082c7d8cAndres Morales } else { 384dceae917c7aeb73dfce10ab5e2e9143fb988c4a8Andres Morales toast (getToastString(R.string.connect_peripheral_failed)); 3857aaafa209be6c1b01081cfb5ed1e9530082c7d8cAndres Morales complete(false); 3867aaafa209be6c1b01081cfb5ed1e9530082c7d8cAndres Morales } 387a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly } 388a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly break; 389a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly } 390a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly } 391a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly 39249913d6a0ed709ff6edde40bb040605997b561f3Nick Pelly void startBonding() { 393a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly mState = STATE_BONDING; 394dceae917c7aeb73dfce10ab5e2e9143fb988c4a8Andres Morales toast(getToastString(R.string.pairing_peripheral)); 395ca75f7282125255caee81f4bcd8f765d709acc38Jakub Pawlowski if (mOobData != null) { 396ca75f7282125255caee81f4bcd8f765d709acc38Jakub Pawlowski if (!mDevice.createBondOutOfBand(mTransport, mOobData)) { 397ca75f7282125255caee81f4bcd8f765d709acc38Jakub Pawlowski toast(getToastString(R.string.pairing_peripheral_failed)); 398ca75f7282125255caee81f4bcd8f765d709acc38Jakub Pawlowski complete(false); 399ca75f7282125255caee81f4bcd8f765d709acc38Jakub Pawlowski } 400ca75f7282125255caee81f4bcd8f765d709acc38Jakub Pawlowski } else if (!mDevice.createBond(mTransport)) { 401ca75f7282125255caee81f4bcd8f765d709acc38Jakub Pawlowski toast(getToastString(R.string.pairing_peripheral_failed)); 402ca75f7282125255caee81f4bcd8f765d709acc38Jakub Pawlowski complete(false); 403a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly } 404a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly } 405a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly 40649913d6a0ed709ff6edde40bb040605997b561f3Nick Pelly void handleIntent(Intent intent) { 407a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly String action = intent.getAction(); 408be1939b4b6003ac7a65fcb95a3912f5e1ce8e75fThe Android Open Source Project // Everything requires the device to match... 409a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE); 410a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly if (!mDevice.equals(device)) return; 411a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly 41278705adb33530d58f6b25bd3976ef36df196af55Martijn Coenen if (ACTION_ALLOW_CONNECT.equals(action)) { 41378705adb33530d58f6b25bd3976ef36df196af55Martijn Coenen nextStepConnect(); 41478705adb33530d58f6b25bd3976ef36df196af55Martijn Coenen } else if (ACTION_DENY_CONNECT.equals(action)) { 41578705adb33530d58f6b25bd3976ef36df196af55Martijn Coenen complete(false); 4167aaafa209be6c1b01081cfb5ed1e9530082c7d8cAndres Morales } else if (BluetoothDevice.ACTION_BOND_STATE_CHANGED.equals(action) 4177aaafa209be6c1b01081cfb5ed1e9530082c7d8cAndres Morales && mState == STATE_BONDING) { 418a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly int bond = intent.getIntExtra(BluetoothDevice.EXTRA_BOND_STATE, 419a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly BluetoothAdapter.ERROR); 420a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly if (bond == BluetoothDevice.BOND_BONDED) { 421a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly nextStepConnect(); 422a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly } else if (bond == BluetoothDevice.BOND_NONE) { 423dceae917c7aeb73dfce10ab5e2e9143fb988c4a8Andres Morales toast(getToastString(R.string.pairing_peripheral_failed)); 424226307d8c2952c42855005d7c0107b42b066bc9aMartijn Coenen complete(false); 425a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly } 426a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly } else if (BluetoothHeadset.ACTION_CONNECTION_STATE_CHANGED.equals(action) && 427a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly (mState == STATE_CONNECTING || mState == STATE_DISCONNECTING)) { 428a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly int state = intent.getIntExtra(BluetoothProfile.EXTRA_STATE, BluetoothAdapter.ERROR); 429a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly if (state == BluetoothProfile.STATE_CONNECTED) { 430a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly mHfpResult = RESULT_CONNECTED; 431a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly nextStep(); 432a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly } else if (state == BluetoothProfile.STATE_DISCONNECTED) { 433a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly mHfpResult = RESULT_DISCONNECTED; 434a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly nextStep(); 435a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly } 436a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly } else if (BluetoothA2dp.ACTION_CONNECTION_STATE_CHANGED.equals(action) && 437a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly (mState == STATE_CONNECTING || mState == STATE_DISCONNECTING)) { 438a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly int state = intent.getIntExtra(BluetoothProfile.EXTRA_STATE, BluetoothAdapter.ERROR); 439a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly if (state == BluetoothProfile.STATE_CONNECTED) { 440a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly mA2dpResult = RESULT_CONNECTED; 441a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly nextStep(); 442a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly } else if (state == BluetoothProfile.STATE_DISCONNECTED) { 443a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly mA2dpResult = RESULT_DISCONNECTED; 444a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly nextStep(); 445a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly } 4467aaafa209be6c1b01081cfb5ed1e9530082c7d8cAndres Morales } else if (BluetoothInputDevice.ACTION_CONNECTION_STATE_CHANGED.equals(action) && 4477aaafa209be6c1b01081cfb5ed1e9530082c7d8cAndres Morales (mState == STATE_CONNECTING || mState == STATE_DISCONNECTING)) { 4487aaafa209be6c1b01081cfb5ed1e9530082c7d8cAndres Morales int state = intent.getIntExtra(BluetoothProfile.EXTRA_STATE, BluetoothAdapter.ERROR); 4497aaafa209be6c1b01081cfb5ed1e9530082c7d8cAndres Morales if (state == BluetoothProfile.STATE_CONNECTED) { 4507aaafa209be6c1b01081cfb5ed1e9530082c7d8cAndres Morales mHidResult = RESULT_CONNECTED; 4517aaafa209be6c1b01081cfb5ed1e9530082c7d8cAndres Morales nextStep(); 4527aaafa209be6c1b01081cfb5ed1e9530082c7d8cAndres Morales } else if (state == BluetoothProfile.STATE_DISCONNECTED) { 4537aaafa209be6c1b01081cfb5ed1e9530082c7d8cAndres Morales mHidResult = RESULT_DISCONNECTED; 4547aaafa209be6c1b01081cfb5ed1e9530082c7d8cAndres Morales nextStep(); 4557aaafa209be6c1b01081cfb5ed1e9530082c7d8cAndres Morales } 456a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly } 457a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly } 458a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly 459226307d8c2952c42855005d7c0107b42b066bc9aMartijn Coenen void complete(boolean connected) { 460a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly if (DBG) Log.d(TAG, "complete()"); 461a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly mState = STATE_COMPLETE; 462a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly mContext.unregisterReceiver(mReceiver); 463a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly mHandler.removeMessages(MSG_TIMEOUT); 464525c260303268a83da4c3413b953d13c9084e834The Android Open Source Project synchronized (mLock) { 465525c260303268a83da4c3413b953d13c9084e834The Android Open Source Project if (mA2dp != null) { 466525c260303268a83da4c3413b953d13c9084e834The Android Open Source Project mBluetoothAdapter.closeProfileProxy(BluetoothProfile.A2DP, mA2dp); 467525c260303268a83da4c3413b953d13c9084e834The Android Open Source Project } 468525c260303268a83da4c3413b953d13c9084e834The Android Open Source Project if (mHeadset != null) { 469525c260303268a83da4c3413b953d13c9084e834The Android Open Source Project mBluetoothAdapter.closeProfileProxy(BluetoothProfile.HEADSET, mHeadset); 470525c260303268a83da4c3413b953d13c9084e834The Android Open Source Project } 4717aaafa209be6c1b01081cfb5ed1e9530082c7d8cAndres Morales 4727aaafa209be6c1b01081cfb5ed1e9530082c7d8cAndres Morales if (mInput != null) { 4737aaafa209be6c1b01081cfb5ed1e9530082c7d8cAndres Morales mBluetoothAdapter.closeProfileProxy(BluetoothProfile.INPUT_DEVICE, mInput); 4747aaafa209be6c1b01081cfb5ed1e9530082c7d8cAndres Morales } 4757aaafa209be6c1b01081cfb5ed1e9530082c7d8cAndres Morales 476525c260303268a83da4c3413b953d13c9084e834The Android Open Source Project mA2dp = null; 477525c260303268a83da4c3413b953d13c9084e834The Android Open Source Project mHeadset = null; 4787aaafa209be6c1b01081cfb5ed1e9530082c7d8cAndres Morales mInput = null; 479525c260303268a83da4c3413b953d13c9084e834The Android Open Source Project } 4807aaafa209be6c1b01081cfb5ed1e9530082c7d8cAndres Morales mCallback.onBluetoothPeripheralHandoverComplete(connected); 481a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly } 482a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly 483a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly void toast(CharSequence text) { 48449913d6a0ed709ff6edde40bb040605997b561f3Nick Pelly Toast.makeText(mContext, text, Toast.LENGTH_SHORT).show(); 48549913d6a0ed709ff6edde40bb040605997b561f3Nick Pelly } 48649913d6a0ed709ff6edde40bb040605997b561f3Nick Pelly 48749913d6a0ed709ff6edde40bb040605997b561f3Nick Pelly void startTheMusic() { 4885f46ed88ca42cbe7a8930421775d4b8d69f4aaf6RoboErik MediaSessionLegacyHelper helper = MediaSessionLegacyHelper.getHelper(mContext); 4895f46ed88ca42cbe7a8930421775d4b8d69f4aaf6RoboErik if (helper != null) { 4905f46ed88ca42cbe7a8930421775d4b8d69f4aaf6RoboErik KeyEvent keyEvent = new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_MEDIA_PLAY); 4915f46ed88ca42cbe7a8930421775d4b8d69f4aaf6RoboErik helper.sendMediaButtonEvent(keyEvent, false); 4925f46ed88ca42cbe7a8930421775d4b8d69f4aaf6RoboErik keyEvent = new KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_MEDIA_PLAY); 4935f46ed88ca42cbe7a8930421775d4b8d69f4aaf6RoboErik helper.sendMediaButtonEvent(keyEvent, false); 494825f3b96bd360f42158a25e5d6b09fc6f046ab10Martijn Coenen } else { 4955f46ed88ca42cbe7a8930421775d4b8d69f4aaf6RoboErik Log.w(TAG, "Unable to send media key event"); 496825f3b96bd360f42158a25e5d6b09fc6f046ab10Martijn Coenen } 497a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly } 498a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly 49978705adb33530d58f6b25bd3976ef36df196af55Martijn Coenen void requestPairConfirmation() { 50078705adb33530d58f6b25bd3976ef36df196af55Martijn Coenen Intent dialogIntent = new Intent(mContext, ConfirmConnectActivity.class); 501f291ad8974438d513757d5da929a86cd3d86fe45Martijn Coenen dialogIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK); 50278705adb33530d58f6b25bd3976ef36df196af55Martijn Coenen dialogIntent.putExtra(BluetoothDevice.EXTRA_DEVICE, mDevice); 50378705adb33530d58f6b25bd3976ef36df196af55Martijn Coenen 504be1939b4b6003ac7a65fcb95a3912f5e1ce8e75fThe Android Open Source Project mContext.startActivity(dialogIntent); 50578705adb33530d58f6b25bd3976ef36df196af55Martijn Coenen } 50678705adb33530d58f6b25bd3976ef36df196af55Martijn Coenen 507a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly final Handler mHandler = new Handler() { 508a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly @Override 509a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly public void handleMessage(Message msg) { 510a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly switch (msg.what) { 511a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly case MSG_TIMEOUT: 51249913d6a0ed709ff6edde40bb040605997b561f3Nick Pelly if (mState == STATE_COMPLETE) return; 51349913d6a0ed709ff6edde40bb040605997b561f3Nick Pelly Log.i(TAG, "Timeout completing BT handover"); 514226307d8c2952c42855005d7c0107b42b066bc9aMartijn Coenen complete(false); 515a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly break; 516525c260303268a83da4c3413b953d13c9084e834The Android Open Source Project case MSG_NEXT_STEP: 517525c260303268a83da4c3413b953d13c9084e834The Android Open Source Project nextStep(); 518525c260303268a83da4c3413b953d13c9084e834The Android Open Source Project break; 519a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly } 520a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly } 521a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly }; 522a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly 52349913d6a0ed709ff6edde40bb040605997b561f3Nick Pelly final BroadcastReceiver mReceiver = new BroadcastReceiver() { 52449913d6a0ed709ff6edde40bb040605997b561f3Nick Pelly @Override 52549913d6a0ed709ff6edde40bb040605997b561f3Nick Pelly public void onReceive(Context context, Intent intent) { 52649913d6a0ed709ff6edde40bb040605997b561f3Nick Pelly handleIntent(intent); 52749913d6a0ed709ff6edde40bb040605997b561f3Nick Pelly } 52849913d6a0ed709ff6edde40bb040605997b561f3Nick Pelly }; 52949913d6a0ed709ff6edde40bb040605997b561f3Nick Pelly 53049913d6a0ed709ff6edde40bb040605997b561f3Nick Pelly static void checkMainThread() { 53149913d6a0ed709ff6edde40bb040605997b561f3Nick Pelly if (Looper.myLooper() != Looper.getMainLooper()) { 53249913d6a0ed709ff6edde40bb040605997b561f3Nick Pelly throw new IllegalThreadStateException("must be called on main thread"); 53349913d6a0ed709ff6edde40bb040605997b561f3Nick Pelly } 534a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly } 535525c260303268a83da4c3413b953d13c9084e834The Android Open Source Project 536525c260303268a83da4c3413b953d13c9084e834The Android Open Source Project @Override 537525c260303268a83da4c3413b953d13c9084e834The Android Open Source Project public void onServiceConnected(int profile, BluetoothProfile proxy) { 538525c260303268a83da4c3413b953d13c9084e834The Android Open Source Project synchronized (mLock) { 539525c260303268a83da4c3413b953d13c9084e834The Android Open Source Project switch (profile) { 540525c260303268a83da4c3413b953d13c9084e834The Android Open Source Project case BluetoothProfile.HEADSET: 541525c260303268a83da4c3413b953d13c9084e834The Android Open Source Project mHeadset = (BluetoothHeadset) proxy; 542525c260303268a83da4c3413b953d13c9084e834The Android Open Source Project if (mA2dp != null) { 543525c260303268a83da4c3413b953d13c9084e834The Android Open Source Project mHandler.sendEmptyMessage(MSG_NEXT_STEP); 544525c260303268a83da4c3413b953d13c9084e834The Android Open Source Project } 545525c260303268a83da4c3413b953d13c9084e834The Android Open Source Project break; 546525c260303268a83da4c3413b953d13c9084e834The Android Open Source Project case BluetoothProfile.A2DP: 547525c260303268a83da4c3413b953d13c9084e834The Android Open Source Project mA2dp = (BluetoothA2dp) proxy; 548525c260303268a83da4c3413b953d13c9084e834The Android Open Source Project if (mHeadset != null) { 549525c260303268a83da4c3413b953d13c9084e834The Android Open Source Project mHandler.sendEmptyMessage(MSG_NEXT_STEP); 550525c260303268a83da4c3413b953d13c9084e834The Android Open Source Project } 551525c260303268a83da4c3413b953d13c9084e834The Android Open Source Project break; 5527aaafa209be6c1b01081cfb5ed1e9530082c7d8cAndres Morales case BluetoothProfile.INPUT_DEVICE: 5537aaafa209be6c1b01081cfb5ed1e9530082c7d8cAndres Morales mInput = (BluetoothInputDevice) proxy; 5547aaafa209be6c1b01081cfb5ed1e9530082c7d8cAndres Morales if (mInput != null) { 5557aaafa209be6c1b01081cfb5ed1e9530082c7d8cAndres Morales mHandler.sendEmptyMessage(MSG_NEXT_STEP); 5567aaafa209be6c1b01081cfb5ed1e9530082c7d8cAndres Morales } 5577aaafa209be6c1b01081cfb5ed1e9530082c7d8cAndres Morales break; 558525c260303268a83da4c3413b953d13c9084e834The Android Open Source Project } 559525c260303268a83da4c3413b953d13c9084e834The Android Open Source Project } 560525c260303268a83da4c3413b953d13c9084e834The Android Open Source Project } 561525c260303268a83da4c3413b953d13c9084e834The Android Open Source Project 562525c260303268a83da4c3413b953d13c9084e834The Android Open Source Project @Override 563525c260303268a83da4c3413b953d13c9084e834The Android Open Source Project public void onServiceDisconnected(int profile) { 564525c260303268a83da4c3413b953d13c9084e834The Android Open Source Project // We can ignore these 565525c260303268a83da4c3413b953d13c9084e834The Android Open Source Project } 566a2908a164eec02c34efc39db2e3ee0e38ebbfdb1Nick Pelly} 567