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