1b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project/*
2b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project * Copyright (C) 2006 The Android Open Source Project
3b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project *
4b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project * Licensed under the Apache License, Version 2.0 (the "License");
5b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project * you may not use this file except in compliance with the License.
6b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project * You may obtain a copy of the License at
7b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project *
8b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project *      http://www.apache.org/licenses/LICENSE-2.0
9b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project *
10b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project * Unless required by applicable law or agreed to in writing, software
11b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project * distributed under the License is distributed on an "AS IS" BASIS,
12b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project * See the License for the specific language governing permissions and
14b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project * limitations under the License.
15b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project */
16b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project
17b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Projectpackage com.android.phone;
18b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project
19b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Projectimport android.app.Service;
20db065e9834bcad9b0cb16c272b4b272dee8bdf62Nick Pellyimport android.bluetooth.BluetoothAdapter;
21b77350c34a4cc1a5ac9d27d20d4c925524bc3ba1Jaikumar Ganeshimport android.bluetooth.BluetoothAudioGateway;
22b77350c34a4cc1a5ac9d27d20d4c925524bc3ba1Jaikumar Ganeshimport android.bluetooth.BluetoothAudioGateway.IncomingConnectionInfo;
23b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Projectimport android.bluetooth.BluetoothDevice;
24b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Projectimport android.bluetooth.BluetoothHeadset;
251d417d7ede71bbcf72a7edebf528efbfcf9b82bbJaikumar Ganeshimport android.bluetooth.BluetoothProfile;
2627a51be4c5df0423533b9e976bc9ec7c39c53017Jaikumar Ganeshimport android.bluetooth.BluetoothUuid;
27b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Projectimport android.bluetooth.HeadsetBase;
28b77350c34a4cc1a5ac9d27d20d4c925524bc3ba1Jaikumar Ganeshimport android.bluetooth.IBluetooth;
29b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Projectimport android.bluetooth.IBluetoothHeadset;
30b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Projectimport android.content.BroadcastReceiver;
31b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Projectimport android.content.Context;
32b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Projectimport android.content.Intent;
33b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Projectimport android.content.IntentFilter;
34b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Projectimport android.media.AudioManager;
35b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Projectimport android.os.Handler;
36b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Projectimport android.os.IBinder;
37b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Projectimport android.os.Message;
38b77350c34a4cc1a5ac9d27d20d4c925524bc3ba1Jaikumar Ganeshimport android.os.ParcelUuid;
39b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Projectimport android.os.PowerManager;
40b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Projectimport android.os.RemoteException;
41b77350c34a4cc1a5ac9d27d20d4c925524bc3ba1Jaikumar Ganeshimport android.os.ServiceManager;
42b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Projectimport android.provider.Settings;
43b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Projectimport android.util.Log;
44b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project
451d417d7ede71bbcf72a7edebf528efbfcf9b82bbJaikumar Ganeshimport java.util.ArrayList;
467893303ffaf4c2e1dee13fb7cf06525502c03ba1Jaikumar Ganeshimport java.util.List;
47d9cd25a4dde8ec303893815d0a6a61c824c0bc5cMatthew Xieimport java.util.concurrent.ConcurrentHashMap;
48b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project
49b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project/**
50b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project * Provides Bluetooth Headset and Handsfree profile, as a service in
51b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project * the Phone application.
52b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project * @hide
53b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project */
54b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Projectpublic class BluetoothHeadsetService extends Service {
551d417d7ede71bbcf72a7edebf528efbfcf9b82bbJaikumar Ganesh    private static final String TAG = "Bluetooth HSHFP";
56b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project    private static final boolean DBG = true;
57b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project
58b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project    private static final String PREF_NAME = BluetoothHeadsetService.class.getSimpleName();
59b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project    private static final String PREF_LAST_HEADSET = "lastHeadsetAddress";
60b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project
61b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project    private static final int PHONE_STATE_CHANGED = 1;
62b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project
63b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project    private static final String BLUETOOTH_ADMIN_PERM = android.Manifest.permission.BLUETOOTH_ADMIN;
64b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project    private static final String BLUETOOTH_PERM = android.Manifest.permission.BLUETOOTH;
65b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project
66b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project    private static boolean sHasStarted = false;
67b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project
68a1b11e1bafaaebeb4720d9c90a416c3af694d1a1Jaikumar Ganesh    private BluetoothDevice mDeviceSdpQuery;
69db065e9834bcad9b0cb16c272b4b272dee8bdf62Nick Pelly    private BluetoothAdapter mAdapter;
70b77350c34a4cc1a5ac9d27d20d4c925524bc3ba1Jaikumar Ganesh    private IBluetooth mBluetoothService;
71b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project    private PowerManager mPowerManager;
72b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project    private BluetoothAudioGateway mAg;
73b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project    private BluetoothHandsfree mBtHandsfree;
74d9cd25a4dde8ec303893815d0a6a61c824c0bc5cMatthew Xie    private ConcurrentHashMap<BluetoothDevice, BluetoothRemoteHeadset> mRemoteHeadsets;
751d417d7ede71bbcf72a7edebf528efbfcf9b82bbJaikumar Ganesh    private BluetoothDevice mAudioConnectedDevice;
76b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project
77b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project    @Override
78b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project    public void onCreate() {
79b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project        super.onCreate();
80cec56587c1d3c90014848d7adfe817bef9c325eaNick Pelly        mAdapter = BluetoothAdapter.getDefaultAdapter();
81db065e9834bcad9b0cb16c272b4b272dee8bdf62Nick Pelly        mPowerManager = (PowerManager) getSystemService(Context.POWER_SERVICE);
82b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project        mBtHandsfree = PhoneApp.getInstance().getBluetoothHandsfree();
83db065e9834bcad9b0cb16c272b4b272dee8bdf62Nick Pelly        mAg = new BluetoothAudioGateway(mAdapter);
84b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project        IntentFilter filter = new IntentFilter(
8518e0a07cc700234f5a02fe10eee9b3ea10db58b3Nick Pelly                BluetoothDevice.ACTION_ACL_DISCONNECT_REQUESTED);
867e4d71c713bba5afea4cbe3e9bd1952ff1725415Nick Pelly        filter.addAction(BluetoothAdapter.ACTION_STATE_CHANGED);
87b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project        filter.addAction(AudioManager.VOLUME_CHANGED_ACTION);
88a1b11e1bafaaebeb4720d9c90a416c3af694d1a1Jaikumar Ganesh        filter.addAction(BluetoothDevice.ACTION_UUID);
897e4d71c713bba5afea4cbe3e9bd1952ff1725415Nick Pelly        registerReceiver(mBluetoothReceiver, filter);
90b77350c34a4cc1a5ac9d27d20d4c925524bc3ba1Jaikumar Ganesh
91b77350c34a4cc1a5ac9d27d20d4c925524bc3ba1Jaikumar Ganesh        IBinder b = ServiceManager.getService(BluetoothAdapter.BLUETOOTH_SERVICE);
92b77350c34a4cc1a5ac9d27d20d4c925524bc3ba1Jaikumar Ganesh        if (b == null) {
93b77350c34a4cc1a5ac9d27d20d4c925524bc3ba1Jaikumar Ganesh            throw new RuntimeException("Bluetooth service not available");
94b77350c34a4cc1a5ac9d27d20d4c925524bc3ba1Jaikumar Ganesh        }
95b77350c34a4cc1a5ac9d27d20d4c925524bc3ba1Jaikumar Ganesh        mBluetoothService = IBluetooth.Stub.asInterface(b);
96d9cd25a4dde8ec303893815d0a6a61c824c0bc5cMatthew Xie        mRemoteHeadsets = new ConcurrentHashMap<BluetoothDevice, BluetoothRemoteHeadset>();
97b77350c34a4cc1a5ac9d27d20d4c925524bc3ba1Jaikumar Ganesh   }
98b77350c34a4cc1a5ac9d27d20d4c925524bc3ba1Jaikumar Ganesh
99b77350c34a4cc1a5ac9d27d20d4c925524bc3ba1Jaikumar Ganesh   private class BluetoothRemoteHeadset {
100b77350c34a4cc1a5ac9d27d20d4c925524bc3ba1Jaikumar Ganesh       private int mState;
101912186b04d22881fd025a7cbe586228b85721a05Jaikumar Ganesh       private int mAudioState;
102b77350c34a4cc1a5ac9d27d20d4c925524bc3ba1Jaikumar Ganesh       private int mHeadsetType;
103b77350c34a4cc1a5ac9d27d20d4c925524bc3ba1Jaikumar Ganesh       private HeadsetBase mHeadset;
104b77350c34a4cc1a5ac9d27d20d4c925524bc3ba1Jaikumar Ganesh       private IncomingConnectionInfo mIncomingInfo;
105b77350c34a4cc1a5ac9d27d20d4c925524bc3ba1Jaikumar Ganesh
106b77350c34a4cc1a5ac9d27d20d4c925524bc3ba1Jaikumar Ganesh       BluetoothRemoteHeadset() {
1071d417d7ede71bbcf72a7edebf528efbfcf9b82bbJaikumar Ganesh           mState = BluetoothProfile.STATE_DISCONNECTED;
108b77350c34a4cc1a5ac9d27d20d4c925524bc3ba1Jaikumar Ganesh           mHeadsetType = BluetoothHandsfree.TYPE_UNKNOWN;
109b77350c34a4cc1a5ac9d27d20d4c925524bc3ba1Jaikumar Ganesh           mHeadset = null;
110b77350c34a4cc1a5ac9d27d20d4c925524bc3ba1Jaikumar Ganesh           mIncomingInfo = null;
111912186b04d22881fd025a7cbe586228b85721a05Jaikumar Ganesh           mAudioState = BluetoothHeadset.STATE_AUDIO_DISCONNECTED;
112b77350c34a4cc1a5ac9d27d20d4c925524bc3ba1Jaikumar Ganesh       }
113b77350c34a4cc1a5ac9d27d20d4c925524bc3ba1Jaikumar Ganesh
114b77350c34a4cc1a5ac9d27d20d4c925524bc3ba1Jaikumar Ganesh       BluetoothRemoteHeadset(int headsetType, IncomingConnectionInfo incomingInfo) {
1151d417d7ede71bbcf72a7edebf528efbfcf9b82bbJaikumar Ganesh           mState = BluetoothProfile.STATE_DISCONNECTED;
116b77350c34a4cc1a5ac9d27d20d4c925524bc3ba1Jaikumar Ganesh           mHeadsetType = headsetType;
117b77350c34a4cc1a5ac9d27d20d4c925524bc3ba1Jaikumar Ganesh           mHeadset = null;
118b77350c34a4cc1a5ac9d27d20d4c925524bc3ba1Jaikumar Ganesh           mIncomingInfo = incomingInfo;
119912186b04d22881fd025a7cbe586228b85721a05Jaikumar Ganesh           mAudioState = BluetoothHeadset.STATE_AUDIO_DISCONNECTED;
120b77350c34a4cc1a5ac9d27d20d4c925524bc3ba1Jaikumar Ganesh       }
121b77350c34a4cc1a5ac9d27d20d4c925524bc3ba1Jaikumar Ganesh   }
122b77350c34a4cc1a5ac9d27d20d4c925524bc3ba1Jaikumar Ganesh
123b77350c34a4cc1a5ac9d27d20d4c925524bc3ba1Jaikumar Ganesh   synchronized private BluetoothDevice getCurrentDevice() {
124b77350c34a4cc1a5ac9d27d20d4c925524bc3ba1Jaikumar Ganesh       for (BluetoothDevice device : mRemoteHeadsets.keySet()) {
125b77350c34a4cc1a5ac9d27d20d4c925524bc3ba1Jaikumar Ganesh           int state = mRemoteHeadsets.get(device).mState;
1261d417d7ede71bbcf72a7edebf528efbfcf9b82bbJaikumar Ganesh           if (state == BluetoothProfile.STATE_CONNECTING ||
1271d417d7ede71bbcf72a7edebf528efbfcf9b82bbJaikumar Ganesh               state == BluetoothProfile.STATE_CONNECTED) {
128b77350c34a4cc1a5ac9d27d20d4c925524bc3ba1Jaikumar Ganesh               return device;
129b77350c34a4cc1a5ac9d27d20d4c925524bc3ba1Jaikumar Ganesh           }
130b77350c34a4cc1a5ac9d27d20d4c925524bc3ba1Jaikumar Ganesh       }
131b77350c34a4cc1a5ac9d27d20d4c925524bc3ba1Jaikumar Ganesh       return null;
1326fd5d0533471dcb151401e2f0264ca7d77f32f7dJaikumar Ganesh   }
133b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project
134b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project    @Override
135b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project    public void onStart(Intent intent, int startId) {
136db065e9834bcad9b0cb16c272b4b272dee8bdf62Nick Pelly         if (mAdapter == null) {
137b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project            Log.w(TAG, "Stopping BluetoothHeadsetService: device does not have BT");
138b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project            stopSelf();
139b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project        } else {
140b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project            if (!sHasStarted) {
141b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                if (DBG) log("Starting BluetoothHeadsetService");
142db065e9834bcad9b0cb16c272b4b272dee8bdf62Nick Pelly                if (mAdapter.isEnabled()) {
143b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                    mAg.start(mIncomingConnectionHandler);
144b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                    mBtHandsfree.onBluetoothEnabled();
145b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                }
146b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                sHasStarted = true;
147b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project            }
148b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project        }
149b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project    }
150b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project
151b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project    private final Handler mIncomingConnectionHandler = new Handler() {
152b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project        @Override
153b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project        public void handleMessage(Message msg) {
154b77350c34a4cc1a5ac9d27d20d4c925524bc3ba1Jaikumar Ganesh            synchronized(BluetoothHeadsetService.this) {
155b77350c34a4cc1a5ac9d27d20d4c925524bc3ba1Jaikumar Ganesh                IncomingConnectionInfo info = (IncomingConnectionInfo)msg.obj;
156b77350c34a4cc1a5ac9d27d20d4c925524bc3ba1Jaikumar Ganesh                int type = BluetoothHandsfree.TYPE_UNKNOWN;
157b77350c34a4cc1a5ac9d27d20d4c925524bc3ba1Jaikumar Ganesh                switch(msg.what) {
158b77350c34a4cc1a5ac9d27d20d4c925524bc3ba1Jaikumar Ganesh                case BluetoothAudioGateway.MSG_INCOMING_HEADSET_CONNECTION:
159b77350c34a4cc1a5ac9d27d20d4c925524bc3ba1Jaikumar Ganesh                    type = BluetoothHandsfree.TYPE_HEADSET;
160b77350c34a4cc1a5ac9d27d20d4c925524bc3ba1Jaikumar Ganesh                    break;
161b77350c34a4cc1a5ac9d27d20d4c925524bc3ba1Jaikumar Ganesh                case BluetoothAudioGateway.MSG_INCOMING_HANDSFREE_CONNECTION:
162b77350c34a4cc1a5ac9d27d20d4c925524bc3ba1Jaikumar Ganesh                    type = BluetoothHandsfree.TYPE_HANDSFREE;
163b77350c34a4cc1a5ac9d27d20d4c925524bc3ba1Jaikumar Ganesh                    break;
164b77350c34a4cc1a5ac9d27d20d4c925524bc3ba1Jaikumar Ganesh                }
165b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project
166b77350c34a4cc1a5ac9d27d20d4c925524bc3ba1Jaikumar Ganesh                Log.i(TAG, "Incoming rfcomm (" + BluetoothHandsfree.typeToString(type) +
167b77350c34a4cc1a5ac9d27d20d4c925524bc3ba1Jaikumar Ganesh                      ") connection from " + info.mRemoteDevice + "on channel " +
168b77350c34a4cc1a5ac9d27d20d4c925524bc3ba1Jaikumar Ganesh                      info.mRfcommChan);
169b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project
1701d417d7ede71bbcf72a7edebf528efbfcf9b82bbJaikumar Ganesh                int priority = BluetoothProfile.PRIORITY_OFF;
171b77350c34a4cc1a5ac9d27d20d4c925524bc3ba1Jaikumar Ganesh                HeadsetBase headset;
172b77350c34a4cc1a5ac9d27d20d4c925524bc3ba1Jaikumar Ganesh                priority = getPriority(info.mRemoteDevice);
1731d417d7ede71bbcf72a7edebf528efbfcf9b82bbJaikumar Ganesh                if (priority <= BluetoothProfile.PRIORITY_OFF) {
174b77350c34a4cc1a5ac9d27d20d4c925524bc3ba1Jaikumar Ganesh                    Log.i(TAG, "Rejecting incoming connection because priority = " + priority);
17527a51be4c5df0423533b9e976bc9ec7c39c53017Jaikumar Ganesh
1761498a77b9d76318817fe5a1e3a961070ead26fadHerb Jellinek                    headset = new HeadsetBase(mPowerManager, mAdapter,
1771498a77b9d76318817fe5a1e3a961070ead26fadHerb Jellinek                                              info.mRemoteDevice,
1781498a77b9d76318817fe5a1e3a961070ead26fadHerb Jellinek                                              info.mSocketFd, info.mRfcommChan,
1791498a77b9d76318817fe5a1e3a961070ead26fadHerb Jellinek                                              null);
180f28ab7c8b2dcaeb1520a920979c0238c60fafd25Nick Pelly                    headset.disconnect();
181cf6512686414b26f28554598364c501e38923f21Matthew Xie                    try {
182cf6512686414b26f28554598364c501e38923f21Matthew Xie                        mBluetoothService.notifyIncomingConnection(info.mRemoteDevice.getAddress(),
183cf6512686414b26f28554598364c501e38923f21Matthew Xie                                                                   true);
184cf6512686414b26f28554598364c501e38923f21Matthew Xie                    } catch (RemoteException e) {
185cf6512686414b26f28554598364c501e38923f21Matthew Xie                        Log.e(TAG, "notifyIncomingConnection", e);
186cf6512686414b26f28554598364c501e38923f21Matthew Xie                    }
187b77350c34a4cc1a5ac9d27d20d4c925524bc3ba1Jaikumar Ganesh                    return;
188b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                }
189b77350c34a4cc1a5ac9d27d20d4c925524bc3ba1Jaikumar Ganesh
190b77350c34a4cc1a5ac9d27d20d4c925524bc3ba1Jaikumar Ganesh                BluetoothRemoteHeadset remoteHeadset;
191b77350c34a4cc1a5ac9d27d20d4c925524bc3ba1Jaikumar Ganesh                BluetoothDevice device = getCurrentDevice();
192b77350c34a4cc1a5ac9d27d20d4c925524bc3ba1Jaikumar Ganesh
1931d417d7ede71bbcf72a7edebf528efbfcf9b82bbJaikumar Ganesh                int state = BluetoothProfile.STATE_DISCONNECTED;
194b77350c34a4cc1a5ac9d27d20d4c925524bc3ba1Jaikumar Ganesh                if (device != null) {
195b77350c34a4cc1a5ac9d27d20d4c925524bc3ba1Jaikumar Ganesh                    state = mRemoteHeadsets.get(device).mState;
196b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                }
197b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project
198b77350c34a4cc1a5ac9d27d20d4c925524bc3ba1Jaikumar Ganesh                switch (state) {
1991d417d7ede71bbcf72a7edebf528efbfcf9b82bbJaikumar Ganesh                case BluetoothProfile.STATE_DISCONNECTED:
200b77350c34a4cc1a5ac9d27d20d4c925524bc3ba1Jaikumar Ganesh                    // headset connecting us, lets join
201b77350c34a4cc1a5ac9d27d20d4c925524bc3ba1Jaikumar Ganesh                    remoteHeadset = new BluetoothRemoteHeadset(type, info);
202b77350c34a4cc1a5ac9d27d20d4c925524bc3ba1Jaikumar Ganesh                    mRemoteHeadsets.put(info.mRemoteDevice, remoteHeadset);
203b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project
204b77350c34a4cc1a5ac9d27d20d4c925524bc3ba1Jaikumar Ganesh                    try {
205b77350c34a4cc1a5ac9d27d20d4c925524bc3ba1Jaikumar Ganesh                        mBluetoothService.notifyIncomingConnection(
206cf6512686414b26f28554598364c501e38923f21Matthew Xie                           info.mRemoteDevice.getAddress(), false);
207b77350c34a4cc1a5ac9d27d20d4c925524bc3ba1Jaikumar Ganesh                    } catch (RemoteException e) {
208b77350c34a4cc1a5ac9d27d20d4c925524bc3ba1Jaikumar Ganesh                        Log.e(TAG, "notifyIncomingConnection");
209b77350c34a4cc1a5ac9d27d20d4c925524bc3ba1Jaikumar Ganesh                    }
210b77350c34a4cc1a5ac9d27d20d4c925524bc3ba1Jaikumar Ganesh                    break;
2111d417d7ede71bbcf72a7edebf528efbfcf9b82bbJaikumar Ganesh                case BluetoothProfile.STATE_CONNECTING:
212b77350c34a4cc1a5ac9d27d20d4c925524bc3ba1Jaikumar Ganesh                    if (!info.mRemoteDevice.equals(device)) {
213b77350c34a4cc1a5ac9d27d20d4c925524bc3ba1Jaikumar Ganesh                        // different headset, ignoring
214b77350c34a4cc1a5ac9d27d20d4c925524bc3ba1Jaikumar Ganesh                        Log.i(TAG, "Already attempting connect to " + device +
215b77350c34a4cc1a5ac9d27d20d4c925524bc3ba1Jaikumar Ganesh                              ", disconnecting " + info.mRemoteDevice);
216b77350c34a4cc1a5ac9d27d20d4c925524bc3ba1Jaikumar Ganesh
217870bd7f09582278afaf7447a3be72653a742b1bdJean-Baptiste Queru                        headset = new HeadsetBase(mPowerManager, mAdapter,
218870bd7f09582278afaf7447a3be72653a742b1bdJean-Baptiste Queru                                                  info.mRemoteDevice,
219870bd7f09582278afaf7447a3be72653a742b1bdJean-Baptiste Queru                                                  info.mSocketFd, info.mRfcommChan,
220870bd7f09582278afaf7447a3be72653a742b1bdJean-Baptiste Queru                                                  null);
221b77350c34a4cc1a5ac9d27d20d4c925524bc3ba1Jaikumar Ganesh                        headset.disconnect();
222b77350c34a4cc1a5ac9d27d20d4c925524bc3ba1Jaikumar Ganesh                        break;
223b77350c34a4cc1a5ac9d27d20d4c925524bc3ba1Jaikumar Ganesh                    }
224b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project
225b77350c34a4cc1a5ac9d27d20d4c925524bc3ba1Jaikumar Ganesh                    // Incoming and Outgoing connections to the same headset.
226b77350c34a4cc1a5ac9d27d20d4c925524bc3ba1Jaikumar Ganesh                    // The state machine manager will cancel outgoing and accept the incoming one.
227b77350c34a4cc1a5ac9d27d20d4c925524bc3ba1Jaikumar Ganesh                    // Update the state
228b77350c34a4cc1a5ac9d27d20d4c925524bc3ba1Jaikumar Ganesh                    mRemoteHeadsets.get(info.mRemoteDevice).mHeadsetType = type;
229b77350c34a4cc1a5ac9d27d20d4c925524bc3ba1Jaikumar Ganesh                    mRemoteHeadsets.get(info.mRemoteDevice).mIncomingInfo = info;
23027a51be4c5df0423533b9e976bc9ec7c39c53017Jaikumar Ganesh
231b77350c34a4cc1a5ac9d27d20d4c925524bc3ba1Jaikumar Ganesh                    try {
232b77350c34a4cc1a5ac9d27d20d4c925524bc3ba1Jaikumar Ganesh                        mBluetoothService.notifyIncomingConnection(
233cf6512686414b26f28554598364c501e38923f21Matthew Xie                            info.mRemoteDevice.getAddress(), false);
234b77350c34a4cc1a5ac9d27d20d4c925524bc3ba1Jaikumar Ganesh                    } catch (RemoteException e) {
235b77350c34a4cc1a5ac9d27d20d4c925524bc3ba1Jaikumar Ganesh                        Log.e(TAG, "notifyIncomingConnection");
236b77350c34a4cc1a5ac9d27d20d4c925524bc3ba1Jaikumar Ganesh                    }
237b77350c34a4cc1a5ac9d27d20d4c925524bc3ba1Jaikumar Ganesh                    break;
2381d417d7ede71bbcf72a7edebf528efbfcf9b82bbJaikumar Ganesh                case BluetoothProfile.STATE_CONNECTED:
239b77350c34a4cc1a5ac9d27d20d4c925524bc3ba1Jaikumar Ganesh                    Log.i(TAG, "Already connected to " + device + ", disconnecting " +
240b77350c34a4cc1a5ac9d27d20d4c925524bc3ba1Jaikumar Ganesh                            info.mRemoteDevice);
2417d9521b93cadfde1e7fb2e7904641982f1a65bb6Matthew Xie                    rejectIncomingConnection(info);
242b77350c34a4cc1a5ac9d27d20d4c925524bc3ba1Jaikumar Ganesh                    break;
243b77350c34a4cc1a5ac9d27d20d4c925524bc3ba1Jaikumar Ganesh                }
244b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project            }
245b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project        }
246b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project    };
247b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project
2487d9521b93cadfde1e7fb2e7904641982f1a65bb6Matthew Xie    private void rejectIncomingConnection(IncomingConnectionInfo info) {
2497d9521b93cadfde1e7fb2e7904641982f1a65bb6Matthew Xie        HeadsetBase headset = new HeadsetBase(mPowerManager, mAdapter,
2507d9521b93cadfde1e7fb2e7904641982f1a65bb6Matthew Xie            info.mRemoteDevice, info.mSocketFd, info.mRfcommChan, null);
2517d9521b93cadfde1e7fb2e7904641982f1a65bb6Matthew Xie        headset.disconnect();
2527d9521b93cadfde1e7fb2e7904641982f1a65bb6Matthew Xie    }
2537d9521b93cadfde1e7fb2e7904641982f1a65bb6Matthew Xie
2547d9521b93cadfde1e7fb2e7904641982f1a65bb6Matthew Xie
2557e4d71c713bba5afea4cbe3e9bd1952ff1725415Nick Pelly    private final BroadcastReceiver mBluetoothReceiver = new BroadcastReceiver() {
256a1b11e1bafaaebeb4720d9c90a416c3af694d1a1Jaikumar Ganesh
257b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project        @Override
258b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project        public void onReceive(Context context, Intent intent) {
259b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project            String action = intent.getAction();
260db065e9834bcad9b0cb16c272b4b272dee8bdf62Nick Pelly            BluetoothDevice device =
26118e0a07cc700234f5a02fe10eee9b3ea10db58b3Nick Pelly                    intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
262db065e9834bcad9b0cb16c272b4b272dee8bdf62Nick Pelly
263b77350c34a4cc1a5ac9d27d20d4c925524bc3ba1Jaikumar Ganesh            BluetoothDevice currDevice = getCurrentDevice();
2641d417d7ede71bbcf72a7edebf528efbfcf9b82bbJaikumar Ganesh            int state = BluetoothProfile.STATE_DISCONNECTED;
265b77350c34a4cc1a5ac9d27d20d4c925524bc3ba1Jaikumar Ganesh            if (currDevice != null) {
266b77350c34a4cc1a5ac9d27d20d4c925524bc3ba1Jaikumar Ganesh                state = mRemoteHeadsets.get(currDevice).mState;
267b77350c34a4cc1a5ac9d27d20d4c925524bc3ba1Jaikumar Ganesh            }
268b77350c34a4cc1a5ac9d27d20d4c925524bc3ba1Jaikumar Ganesh
2691d417d7ede71bbcf72a7edebf528efbfcf9b82bbJaikumar Ganesh            if ((state == BluetoothProfile.STATE_CONNECTED ||
2701d417d7ede71bbcf72a7edebf528efbfcf9b82bbJaikumar Ganesh                    state == BluetoothProfile.STATE_CONNECTING) &&
27118e0a07cc700234f5a02fe10eee9b3ea10db58b3Nick Pelly                    action.equals(BluetoothDevice.ACTION_ACL_DISCONNECT_REQUESTED) &&
272b77350c34a4cc1a5ac9d27d20d4c925524bc3ba1Jaikumar Ganesh                    device.equals(currDevice)) {
273b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                try {
2741d417d7ede71bbcf72a7edebf528efbfcf9b82bbJaikumar Ganesh                    mBinder.disconnect(currDevice);
275b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                } catch (RemoteException e) {}
2767e4d71c713bba5afea4cbe3e9bd1952ff1725415Nick Pelly            } else if (action.equals(BluetoothAdapter.ACTION_STATE_CHANGED)) {
2777e4d71c713bba5afea4cbe3e9bd1952ff1725415Nick Pelly                switch (intent.getIntExtra(BluetoothAdapter.EXTRA_STATE,
2787593c785145a1b4c19fc2cb46560d5a29731d0caNick Pelly                                           BluetoothAdapter.ERROR)) {
2797e4d71c713bba5afea4cbe3e9bd1952ff1725415Nick Pelly                case BluetoothAdapter.STATE_ON:
2804b8337277ec2e375c3536b97c40e6617a7b12990The Android Open Source Project                    mAg.start(mIncomingConnectionHandler);
2814b8337277ec2e375c3536b97c40e6617a7b12990The Android Open Source Project                    mBtHandsfree.onBluetoothEnabled();
2824b8337277ec2e375c3536b97c40e6617a7b12990The Android Open Source Project                    break;
2837e4d71c713bba5afea4cbe3e9bd1952ff1725415Nick Pelly                case BluetoothAdapter.STATE_TURNING_OFF:
2844b8337277ec2e375c3536b97c40e6617a7b12990The Android Open Source Project                    mBtHandsfree.onBluetoothDisabled();
2854b8337277ec2e375c3536b97c40e6617a7b12990The Android Open Source Project                    mAg.stop();
286b77350c34a4cc1a5ac9d27d20d4c925524bc3ba1Jaikumar Ganesh                    if (currDevice != null) {
287fda63603b954dd7672b002cf3e1ba67c259f15bbJaikumar Ganesh                        try {
288fda63603b954dd7672b002cf3e1ba67c259f15bbJaikumar Ganesh                            mBinder.disconnect(currDevice);
289fda63603b954dd7672b002cf3e1ba67c259f15bbJaikumar Ganesh                        } catch (RemoteException e) {}
290b77350c34a4cc1a5ac9d27d20d4c925524bc3ba1Jaikumar Ganesh                    }
2914b8337277ec2e375c3536b97c40e6617a7b12990The Android Open Source Project                    break;
2924b8337277ec2e375c3536b97c40e6617a7b12990The Android Open Source Project                }
293b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project            } else if (action.equals(AudioManager.VOLUME_CHANGED_ACTION)) {
294b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                int streamType = intent.getIntExtra(AudioManager.EXTRA_VOLUME_STREAM_TYPE, -1);
295b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                if (streamType == AudioManager.STREAM_BLUETOOTH_SCO) {
296b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                    mBtHandsfree.sendScoGainUpdate(intent.getIntExtra(
297b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                            AudioManager.EXTRA_VOLUME_STREAM_VALUE, 0));
298b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                }
299b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project
300a1b11e1bafaaebeb4720d9c90a416c3af694d1a1Jaikumar Ganesh            } else if (action.equals(BluetoothDevice.ACTION_UUID)) {
301b77350c34a4cc1a5ac9d27d20d4c925524bc3ba1Jaikumar Ganesh                if (device.equals(mDeviceSdpQuery) && device.equals(currDevice)) {
302a1b11e1bafaaebeb4720d9c90a416c3af694d1a1Jaikumar Ganesh                    // We have got SDP records for the device we are interested in.
303b77350c34a4cc1a5ac9d27d20d4c925524bc3ba1Jaikumar Ganesh                    getSdpRecordsAndConnect(device);
304a1b11e1bafaaebeb4720d9c90a416c3af694d1a1Jaikumar Ganesh                }
305b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project            }
306b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project        }
307b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project    };
308b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project
309b77350c34a4cc1a5ac9d27d20d4c925524bc3ba1Jaikumar Ganesh    private static final int CONNECT_HEADSET_DELAYED = 1;
310b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project    private Handler mHandler = new Handler() {
311b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project        @Override
312b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project        public void handleMessage(Message msg) {
313b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project            switch (msg.what) {
314b77350c34a4cc1a5ac9d27d20d4c925524bc3ba1Jaikumar Ganesh                case CONNECT_HEADSET_DELAYED:
315b77350c34a4cc1a5ac9d27d20d4c925524bc3ba1Jaikumar Ganesh                    BluetoothDevice device = (BluetoothDevice) msg.obj;
316b77350c34a4cc1a5ac9d27d20d4c925524bc3ba1Jaikumar Ganesh                    getSdpRecordsAndConnect(device);
317b77350c34a4cc1a5ac9d27d20d4c925524bc3ba1Jaikumar Ganesh                    break;
318b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project            }
319b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project        }
320b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project    };
321b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project
322b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project    @Override
323b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project    public IBinder onBind(Intent intent) {
324b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project        return mBinder;
325b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project    }
326b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project
327b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project    // ------------------------------------------------------------------
328b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project    // Bluetooth Headset Connect
329b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project    // ------------------------------------------------------------------
33027a51be4c5df0423533b9e976bc9ec7c39c53017Jaikumar Ganesh    private static final int RFCOMM_CONNECTED             = 1;
33127a51be4c5df0423533b9e976bc9ec7c39c53017Jaikumar Ganesh    private static final int RFCOMM_ERROR                 = 2;
332b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project
333b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project    private long mTimestamp;
334b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project
335b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project    /**
336b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project     * Thread for RFCOMM connection
337b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project     * Messages are sent to mConnectingStatusHandler as connection progresses.
338b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project     */
339b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project    private RfcommConnectThread mConnectThread;
340b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project    private class RfcommConnectThread extends Thread {
341db065e9834bcad9b0cb16c272b4b272dee8bdf62Nick Pelly        private BluetoothDevice device;
342b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project        private int channel;
343b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project        private int type;
34456a7c8d33a5b551aee4ee327762b68063d483914Jaikumar Ganesh
34556a7c8d33a5b551aee4ee327762b68063d483914Jaikumar Ganesh        private static final int EINTERRUPT = -1000;
346a1b11e1bafaaebeb4720d9c90a416c3af694d1a1Jaikumar Ganesh        private static final int ECONNREFUSED = -111;
34756a7c8d33a5b551aee4ee327762b68063d483914Jaikumar Ganesh
348db065e9834bcad9b0cb16c272b4b272dee8bdf62Nick Pelly        public RfcommConnectThread(BluetoothDevice device, int channel, int type) {
349b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project            super();
350db065e9834bcad9b0cb16c272b4b272dee8bdf62Nick Pelly            this.device = device;
351b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project            this.channel = channel;
352b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project            this.type = type;
353b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project        }
354b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project
35556a7c8d33a5b551aee4ee327762b68063d483914Jaikumar Ganesh        private int waitForConnect(HeadsetBase headset) {
35656a7c8d33a5b551aee4ee327762b68063d483914Jaikumar Ganesh            // Try to connect for 20 seconds
357b8be32875b681e8d4727ecddfa3d6a4116adb656Jaikumar Ganesh            int result = 0;
358b8be32875b681e8d4727ecddfa3d6a4116adb656Jaikumar Ganesh            for (int i=0; i < 40 && result == 0; i++) {
359b8be32875b681e8d4727ecddfa3d6a4116adb656Jaikumar Ganesh                // waitForAsyncConnect returns 0 on timeout, 1 on success, < 0 on error.
36056a7c8d33a5b551aee4ee327762b68063d483914Jaikumar Ganesh                result = headset.waitForAsyncConnect(500, mConnectedStatusHandler);
36156a7c8d33a5b551aee4ee327762b68063d483914Jaikumar Ganesh                if (isInterrupted()) {
36256a7c8d33a5b551aee4ee327762b68063d483914Jaikumar Ganesh                    headset.disconnect();
36356a7c8d33a5b551aee4ee327762b68063d483914Jaikumar Ganesh                    return EINTERRUPT;
36456a7c8d33a5b551aee4ee327762b68063d483914Jaikumar Ganesh                }
36556a7c8d33a5b551aee4ee327762b68063d483914Jaikumar Ganesh            }
36656a7c8d33a5b551aee4ee327762b68063d483914Jaikumar Ganesh            return result;
36756a7c8d33a5b551aee4ee327762b68063d483914Jaikumar Ganesh        }
36856a7c8d33a5b551aee4ee327762b68063d483914Jaikumar Ganesh
369b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project        @Override
370b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project        public void run() {
371b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project            long timestamp;
372b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project
373b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project            timestamp = System.currentTimeMillis();
3741498a77b9d76318817fe5a1e3a961070ead26fadHerb Jellinek            HeadsetBase headset = new HeadsetBase(mPowerManager, mAdapter,
3751498a77b9d76318817fe5a1e3a961070ead26fadHerb Jellinek                                                  device, channel);
376b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project
37756a7c8d33a5b551aee4ee327762b68063d483914Jaikumar Ganesh            int result = waitForConnect(headset);
37856a7c8d33a5b551aee4ee327762b68063d483914Jaikumar Ganesh
379c36c9d5d0b168318cce0df0c7316efc7054a5b04Jaikumar Ganesh            if (result != EINTERRUPT && result != 1) {
380a1b11e1bafaaebeb4720d9c90a416c3af694d1a1Jaikumar Ganesh                if (result == ECONNREFUSED && mDeviceSdpQuery == null) {
381a1b11e1bafaaebeb4720d9c90a416c3af694d1a1Jaikumar Ganesh                    // The rfcomm channel number might have changed, do SDP
382a1b11e1bafaaebeb4720d9c90a416c3af694d1a1Jaikumar Ganesh                    // query and try to connect again.
383b77350c34a4cc1a5ac9d27d20d4c925524bc3ba1Jaikumar Ganesh                    mDeviceSdpQuery = getCurrentDevice();
384a1b11e1bafaaebeb4720d9c90a416c3af694d1a1Jaikumar Ganesh                    device.fetchUuidsWithSdp();
385a1b11e1bafaaebeb4720d9c90a416c3af694d1a1Jaikumar Ganesh                    mConnectThread = null;
386a1b11e1bafaaebeb4720d9c90a416c3af694d1a1Jaikumar Ganesh                    return;
387a1b11e1bafaaebeb4720d9c90a416c3af694d1a1Jaikumar Ganesh                } else {
388a1b11e1bafaaebeb4720d9c90a416c3af694d1a1Jaikumar Ganesh                    Log.i(TAG, "Trying to connect to rfcomm socket again after 1 sec");
389a1b11e1bafaaebeb4720d9c90a416c3af694d1a1Jaikumar Ganesh                    try {
390a1b11e1bafaaebeb4720d9c90a416c3af694d1a1Jaikumar Ganesh                      sleep(1000);  // 1 second
3918ef8f988f35aa07cf681cb9ee9f5d6acebb2df2aMatthew Xie                    } catch (InterruptedException e) {
3928ef8f988f35aa07cf681cb9ee9f5d6acebb2df2aMatthew Xie                      return;
3938ef8f988f35aa07cf681cb9ee9f5d6acebb2df2aMatthew Xie                    }
394a1b11e1bafaaebeb4720d9c90a416c3af694d1a1Jaikumar Ganesh                }
39556a7c8d33a5b551aee4ee327762b68063d483914Jaikumar Ganesh                result = waitForConnect(headset);
396b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project            }
397a1b11e1bafaaebeb4720d9c90a416c3af694d1a1Jaikumar Ganesh            mDeviceSdpQuery = null;
39856a7c8d33a5b551aee4ee327762b68063d483914Jaikumar Ganesh            if (result == EINTERRUPT) return;
39956a7c8d33a5b551aee4ee327762b68063d483914Jaikumar Ganesh
400b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project            if (DBG) log("RFCOMM connection attempt took " +
401b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                  (System.currentTimeMillis() - timestamp) + " ms");
402b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project            if (isInterrupted()) {
403b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                headset.disconnect();
404b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                return;
405b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project            }
406b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project            if (result < 0) {
407b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                Log.w(TAG, "headset.waitForAsyncConnect() error: " + result);
408b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                mConnectingStatusHandler.obtainMessage(RFCOMM_ERROR).sendToTarget();
409b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                return;
410b8be32875b681e8d4727ecddfa3d6a4116adb656Jaikumar Ganesh            } else if (result == 0) {
411b8be32875b681e8d4727ecddfa3d6a4116adb656Jaikumar Ganesh                mConnectingStatusHandler.obtainMessage(RFCOMM_ERROR).sendToTarget();
412b8be32875b681e8d4727ecddfa3d6a4116adb656Jaikumar Ganesh                Log.w(TAG, "mHeadset.waitForAsyncConnect() error: " + result + "(timeout)");
413b8be32875b681e8d4727ecddfa3d6a4116adb656Jaikumar Ganesh                return;
414b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project            } else {
415b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                mConnectingStatusHandler.obtainMessage(RFCOMM_CONNECTED, headset).sendToTarget();
416b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project            }
417b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project        }
418b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project    }
419b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project
420b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project    /**
421b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project     * Receives events from mConnectThread back in the main thread.
422b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project     */
423b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project    private final Handler mConnectingStatusHandler = new Handler() {
424b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project        @Override
425b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project        public void handleMessage(Message msg) {
426b77350c34a4cc1a5ac9d27d20d4c925524bc3ba1Jaikumar Ganesh            BluetoothDevice device = getCurrentDevice();
427b77350c34a4cc1a5ac9d27d20d4c925524bc3ba1Jaikumar Ganesh            if (device == null ||
4281d417d7ede71bbcf72a7edebf528efbfcf9b82bbJaikumar Ganesh                mRemoteHeadsets.get(device).mState != BluetoothProfile.STATE_CONNECTING) {
429b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                return;  // stale events
430b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project            }
431b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project
432b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project            switch (msg.what) {
433b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project            case RFCOMM_ERROR:
434b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                if (DBG) log("Rfcomm error");
435b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                mConnectThread = null;
4361d417d7ede71bbcf72a7edebf528efbfcf9b82bbJaikumar Ganesh                setState(device, BluetoothProfile.STATE_DISCONNECTED);
437b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                break;
438b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project            case RFCOMM_CONNECTED:
439b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                if (DBG) log("Rfcomm connected");
440b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                mConnectThread = null;
441b77350c34a4cc1a5ac9d27d20d4c925524bc3ba1Jaikumar Ganesh                HeadsetBase headset = (HeadsetBase)msg.obj;
4421d417d7ede71bbcf72a7edebf528efbfcf9b82bbJaikumar Ganesh                setState(device, BluetoothProfile.STATE_CONNECTED);
443b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project
444b77350c34a4cc1a5ac9d27d20d4c925524bc3ba1Jaikumar Ganesh                mRemoteHeadsets.get(device).mHeadset = headset;
445b77350c34a4cc1a5ac9d27d20d4c925524bc3ba1Jaikumar Ganesh                mBtHandsfree.connectHeadset(headset, mRemoteHeadsets.get(device).mHeadsetType);
446b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                break;
447b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project            }
448b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project        }
449b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project    };
450b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project
451b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project    /**
452b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project     * Receives events from a connected RFCOMM socket back in the main thread.
453b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project     */
454b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project    private final Handler mConnectedStatusHandler = new Handler() {
455b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project        @Override
456b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project        public void handleMessage(Message msg) {
457b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project            switch (msg.what) {
458b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project            case HeadsetBase.RFCOMM_DISCONNECTED:
459bfc44512504ebc93c101ddb394719840f2d25072Jaikumar Ganesh                mBtHandsfree.resetAtState();
460a8b5afa3f830a05fd6679642a6a29dd3265cd273Jaikumar Ganesh                mBtHandsfree.setVirtualCallInProgress(false);
461b77350c34a4cc1a5ac9d27d20d4c925524bc3ba1Jaikumar Ganesh                BluetoothDevice device = getCurrentDevice();
462b77350c34a4cc1a5ac9d27d20d4c925524bc3ba1Jaikumar Ganesh                if (device != null) {
4631d417d7ede71bbcf72a7edebf528efbfcf9b82bbJaikumar Ganesh                    setState(device, BluetoothProfile.STATE_DISCONNECTED);
464b77350c34a4cc1a5ac9d27d20d4c925524bc3ba1Jaikumar Ganesh                }
465b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                break;
466b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project            }
467b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project        }
468b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project    };
469b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project
4701d417d7ede71bbcf72a7edebf528efbfcf9b82bbJaikumar Ganesh    private synchronized void setState(BluetoothDevice device, int state) {
471b77350c34a4cc1a5ac9d27d20d4c925524bc3ba1Jaikumar Ganesh        int prevState = mRemoteHeadsets.get(device).mState;
472b77350c34a4cc1a5ac9d27d20d4c925524bc3ba1Jaikumar Ganesh        if (state != prevState) {
473b77350c34a4cc1a5ac9d27d20d4c925524bc3ba1Jaikumar Ganesh            if (DBG) log("Device: " + device +
4741d417d7ede71bbcf72a7edebf528efbfcf9b82bbJaikumar Ganesh                " Headset  state" + prevState + " -> " + state);
4751d417d7ede71bbcf72a7edebf528efbfcf9b82bbJaikumar Ganesh            if (prevState == BluetoothProfile.STATE_CONNECTED) {
4761d417d7ede71bbcf72a7edebf528efbfcf9b82bbJaikumar Ganesh                // Headset is disconnecting, stop the parser.
477a1478a9074b126d13124c99b7543b2518b3de3b7Eric Laurent                mBtHandsfree.disconnectHeadset();
478b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project            }
4791d417d7ede71bbcf72a7edebf528efbfcf9b82bbJaikumar Ganesh            Intent intent = new Intent(BluetoothHeadset.ACTION_CONNECTION_STATE_CHANGED);
4801d417d7ede71bbcf72a7edebf528efbfcf9b82bbJaikumar Ganesh            intent.putExtra(BluetoothProfile.EXTRA_PREVIOUS_STATE, prevState);
4811d417d7ede71bbcf72a7edebf528efbfcf9b82bbJaikumar Ganesh            intent.putExtra(BluetoothProfile.EXTRA_STATE, state);
482b77350c34a4cc1a5ac9d27d20d4c925524bc3ba1Jaikumar Ganesh            intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device);
4831d417d7ede71bbcf72a7edebf528efbfcf9b82bbJaikumar Ganesh            if (state == BluetoothProfile.STATE_DISCONNECTED) {
484b77350c34a4cc1a5ac9d27d20d4c925524bc3ba1Jaikumar Ganesh                mRemoteHeadsets.get(device).mHeadset = null;
485b77350c34a4cc1a5ac9d27d20d4c925524bc3ba1Jaikumar Ganesh                mRemoteHeadsets.get(device).mHeadsetType = BluetoothHandsfree.TYPE_UNKNOWN;
4864c61a147b28d44c7faa73e75ed55d1aa1aa86a39Jaikumar Ganesh            }
487b77350c34a4cc1a5ac9d27d20d4c925524bc3ba1Jaikumar Ganesh
488b77350c34a4cc1a5ac9d27d20d4c925524bc3ba1Jaikumar Ganesh            mRemoteHeadsets.get(device).mState = state;
489b77350c34a4cc1a5ac9d27d20d4c925524bc3ba1Jaikumar Ganesh
490b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project            sendBroadcast(intent, BLUETOOTH_PERM);
491c9bbb06825360558b81f0595c072194b0ab4a78bJaikumar Ganesh            if (state == BluetoothHeadset.STATE_CONNECTED) {
492a501a53a7d0cfc15ddb25d204b0a813ca77518f5Jaikumar Ganesh                // Set the priority to AUTO_CONNECT
493b77350c34a4cc1a5ac9d27d20d4c925524bc3ba1Jaikumar Ganesh                setPriority(device, BluetoothHeadset.PRIORITY_AUTO_CONNECT);
494c9bbb06825360558b81f0595c072194b0ab4a78bJaikumar Ganesh                adjustOtherHeadsetPriorities(device);
495b77350c34a4cc1a5ac9d27d20d4c925524bc3ba1Jaikumar Ganesh            }
4965500557aeeec455481c30e1f054abb758a92f8a1Jaikumar Ganesh            try {
497c08aaca9ef1d34c83ffed95409f216034335fa40Jaikumar Ganesh                mBluetoothService.sendConnectionStateChange(device, BluetoothProfile.HEADSET,
498c08aaca9ef1d34c83ffed95409f216034335fa40Jaikumar Ganesh                                                            state, prevState);
4995500557aeeec455481c30e1f054abb758a92f8a1Jaikumar Ganesh            } catch (RemoteException e) {
5005500557aeeec455481c30e1f054abb758a92f8a1Jaikumar Ganesh                Log.e(TAG, "sendConnectionStateChange: exception");
5015500557aeeec455481c30e1f054abb758a92f8a1Jaikumar Ganesh            }
502b77350c34a4cc1a5ac9d27d20d4c925524bc3ba1Jaikumar Ganesh       }
503b77350c34a4cc1a5ac9d27d20d4c925524bc3ba1Jaikumar Ganesh    }
504b77350c34a4cc1a5ac9d27d20d4c925524bc3ba1Jaikumar Ganesh
505c9bbb06825360558b81f0595c072194b0ab4a78bJaikumar Ganesh    private void adjustOtherHeadsetPriorities(BluetoothDevice connectedDevice) {
506c9bbb06825360558b81f0595c072194b0ab4a78bJaikumar Ganesh       for (BluetoothDevice device : mAdapter.getBondedDevices()) {
507c9bbb06825360558b81f0595c072194b0ab4a78bJaikumar Ganesh          if (getPriority(device) >= BluetoothHeadset.PRIORITY_AUTO_CONNECT &&
508c9bbb06825360558b81f0595c072194b0ab4a78bJaikumar Ganesh              !device.equals(connectedDevice)) {
509c9bbb06825360558b81f0595c072194b0ab4a78bJaikumar Ganesh              setPriority(device, BluetoothHeadset.PRIORITY_ON);
510c9bbb06825360558b81f0595c072194b0ab4a78bJaikumar Ganesh          }
511c9bbb06825360558b81f0595c072194b0ab4a78bJaikumar Ganesh       }
512c9bbb06825360558b81f0595c072194b0ab4a78bJaikumar Ganesh    }
513c9bbb06825360558b81f0595c072194b0ab4a78bJaikumar Ganesh
514b77350c34a4cc1a5ac9d27d20d4c925524bc3ba1Jaikumar Ganesh    private void setPriority(BluetoothDevice device, int priority) {
515b77350c34a4cc1a5ac9d27d20d4c925524bc3ba1Jaikumar Ganesh        try {
516b77350c34a4cc1a5ac9d27d20d4c925524bc3ba1Jaikumar Ganesh            mBinder.setPriority(device, priority);
517b77350c34a4cc1a5ac9d27d20d4c925524bc3ba1Jaikumar Ganesh        } catch (RemoteException e) {
518b77350c34a4cc1a5ac9d27d20d4c925524bc3ba1Jaikumar Ganesh            Log.e(TAG, "Error while setting priority for: " + device);
519b77350c34a4cc1a5ac9d27d20d4c925524bc3ba1Jaikumar Ganesh        }
520b77350c34a4cc1a5ac9d27d20d4c925524bc3ba1Jaikumar Ganesh    }
521b77350c34a4cc1a5ac9d27d20d4c925524bc3ba1Jaikumar Ganesh
522b77350c34a4cc1a5ac9d27d20d4c925524bc3ba1Jaikumar Ganesh    private int getPriority(BluetoothDevice device) {
523b77350c34a4cc1a5ac9d27d20d4c925524bc3ba1Jaikumar Ganesh        try {
524b77350c34a4cc1a5ac9d27d20d4c925524bc3ba1Jaikumar Ganesh            return mBinder.getPriority(device);
525b77350c34a4cc1a5ac9d27d20d4c925524bc3ba1Jaikumar Ganesh        } catch (RemoteException e) {
526b77350c34a4cc1a5ac9d27d20d4c925524bc3ba1Jaikumar Ganesh            Log.e(TAG, "Error while getting priority for: " + device);
527b77350c34a4cc1a5ac9d27d20d4c925524bc3ba1Jaikumar Ganesh        }
5281d417d7ede71bbcf72a7edebf528efbfcf9b82bbJaikumar Ganesh        return BluetoothProfile.PRIORITY_UNDEFINED;
529b77350c34a4cc1a5ac9d27d20d4c925524bc3ba1Jaikumar Ganesh    }
530b77350c34a4cc1a5ac9d27d20d4c925524bc3ba1Jaikumar Ganesh
531b77350c34a4cc1a5ac9d27d20d4c925524bc3ba1Jaikumar Ganesh    private synchronized void getSdpRecordsAndConnect(BluetoothDevice device) {
532b77350c34a4cc1a5ac9d27d20d4c925524bc3ba1Jaikumar Ganesh        if (!device.equals(getCurrentDevice())) {
533b77350c34a4cc1a5ac9d27d20d4c925524bc3ba1Jaikumar Ganesh            // stale
534b77350c34a4cc1a5ac9d27d20d4c925524bc3ba1Jaikumar Ganesh            return;
535b77350c34a4cc1a5ac9d27d20d4c925524bc3ba1Jaikumar Ganesh        }
5368931676029826b35b9857bf03889fc1f2ff2fda3Jaikumar Ganesh
5378931676029826b35b9857bf03889fc1f2ff2fda3Jaikumar Ganesh        // Check if incoming connection has already connected.
5388931676029826b35b9857bf03889fc1f2ff2fda3Jaikumar Ganesh        if (mRemoteHeadsets.get(device).mState == BluetoothProfile.STATE_CONNECTED) {
5398931676029826b35b9857bf03889fc1f2ff2fda3Jaikumar Ganesh            return;
5408931676029826b35b9857bf03889fc1f2ff2fda3Jaikumar Ganesh        }
5418931676029826b35b9857bf03889fc1f2ff2fda3Jaikumar Ganesh
542b77350c34a4cc1a5ac9d27d20d4c925524bc3ba1Jaikumar Ganesh        ParcelUuid[] uuids = device.getUuids();
5437d059ff40fa0850c2cbec50abeaa30193c4a2cfdJaikumar Ganesh        ParcelUuid[] localUuids = mAdapter.getUuids();
544b77350c34a4cc1a5ac9d27d20d4c925524bc3ba1Jaikumar Ganesh        int type = BluetoothHandsfree.TYPE_UNKNOWN;
54527a51be4c5df0423533b9e976bc9ec7c39c53017Jaikumar Ganesh        if (uuids != null) {
5467d059ff40fa0850c2cbec50abeaa30193c4a2cfdJaikumar Ganesh            if (BluetoothUuid.isUuidPresent(uuids, BluetoothUuid.Handsfree) &&
5477d059ff40fa0850c2cbec50abeaa30193c4a2cfdJaikumar Ganesh                BluetoothUuid.isUuidPresent(localUuids, BluetoothUuid.Handsfree_AG)) {
54827a51be4c5df0423533b9e976bc9ec7c39c53017Jaikumar Ganesh                log("SDP UUID: TYPE_HANDSFREE");
549b77350c34a4cc1a5ac9d27d20d4c925524bc3ba1Jaikumar Ganesh                type = BluetoothHandsfree.TYPE_HANDSFREE;
550b77350c34a4cc1a5ac9d27d20d4c925524bc3ba1Jaikumar Ganesh                mRemoteHeadsets.get(device).mHeadsetType = type;
551b77350c34a4cc1a5ac9d27d20d4c925524bc3ba1Jaikumar Ganesh                int channel = device.getServiceChannel(BluetoothUuid.Handsfree);
552b77350c34a4cc1a5ac9d27d20d4c925524bc3ba1Jaikumar Ganesh                mConnectThread = new RfcommConnectThread(device, channel, type);
55331d8a2e6052729250f9baddf7a31b357a59ffc69Rafal Garbat                if (mAdapter.isDiscovering()) {
55431d8a2e6052729250f9baddf7a31b357a59ffc69Rafal Garbat                    mAdapter.cancelDiscovery();
55531d8a2e6052729250f9baddf7a31b357a59ffc69Rafal Garbat                }
55627a51be4c5df0423533b9e976bc9ec7c39c53017Jaikumar Ganesh                mConnectThread.start();
557c9bbb06825360558b81f0595c072194b0ab4a78bJaikumar Ganesh                if (getPriority(device) < BluetoothHeadset.PRIORITY_AUTO_CONNECT) {
558c9bbb06825360558b81f0595c072194b0ab4a78bJaikumar Ganesh                    setPriority(device, BluetoothHeadset.PRIORITY_AUTO_CONNECT);
559c9bbb06825360558b81f0595c072194b0ab4a78bJaikumar Ganesh                }
56027a51be4c5df0423533b9e976bc9ec7c39c53017Jaikumar Ganesh                return;
5617d059ff40fa0850c2cbec50abeaa30193c4a2cfdJaikumar Ganesh            } else if (BluetoothUuid.isUuidPresent(uuids, BluetoothUuid.HSP) &&
5627d059ff40fa0850c2cbec50abeaa30193c4a2cfdJaikumar Ganesh                BluetoothUuid.isUuidPresent(localUuids, BluetoothUuid.HSP_AG)) {
56327a51be4c5df0423533b9e976bc9ec7c39c53017Jaikumar Ganesh                log("SDP UUID: TYPE_HEADSET");
564b77350c34a4cc1a5ac9d27d20d4c925524bc3ba1Jaikumar Ganesh                type = BluetoothHandsfree.TYPE_HEADSET;
565b77350c34a4cc1a5ac9d27d20d4c925524bc3ba1Jaikumar Ganesh                mRemoteHeadsets.get(device).mHeadsetType = type;
566b77350c34a4cc1a5ac9d27d20d4c925524bc3ba1Jaikumar Ganesh                int channel = device.getServiceChannel(BluetoothUuid.HSP);
567b77350c34a4cc1a5ac9d27d20d4c925524bc3ba1Jaikumar Ganesh                mConnectThread = new RfcommConnectThread(device, channel, type);
56831d8a2e6052729250f9baddf7a31b357a59ffc69Rafal Garbat                if (mAdapter.isDiscovering()) {
56931d8a2e6052729250f9baddf7a31b357a59ffc69Rafal Garbat                    mAdapter.cancelDiscovery();
57031d8a2e6052729250f9baddf7a31b357a59ffc69Rafal Garbat                }
57127a51be4c5df0423533b9e976bc9ec7c39c53017Jaikumar Ganesh                mConnectThread.start();
572c9bbb06825360558b81f0595c072194b0ab4a78bJaikumar Ganesh                if (getPriority(device) < BluetoothHeadset.PRIORITY_AUTO_CONNECT) {
573c9bbb06825360558b81f0595c072194b0ab4a78bJaikumar Ganesh                    setPriority(device, BluetoothHeadset.PRIORITY_AUTO_CONNECT);
574c9bbb06825360558b81f0595c072194b0ab4a78bJaikumar Ganesh                }
57527a51be4c5df0423533b9e976bc9ec7c39c53017Jaikumar Ganesh                return;
57627a51be4c5df0423533b9e976bc9ec7c39c53017Jaikumar Ganesh            }
57727a51be4c5df0423533b9e976bc9ec7c39c53017Jaikumar Ganesh        }
57827a51be4c5df0423533b9e976bc9ec7c39c53017Jaikumar Ganesh        log("SDP UUID: TYPE_UNKNOWN");
579b77350c34a4cc1a5ac9d27d20d4c925524bc3ba1Jaikumar Ganesh        mRemoteHeadsets.get(device).mHeadsetType = type;
5801d417d7ede71bbcf72a7edebf528efbfcf9b82bbJaikumar Ganesh        setState(device, BluetoothProfile.STATE_DISCONNECTED);
58127a51be4c5df0423533b9e976bc9ec7c39c53017Jaikumar Ganesh        return;
58227a51be4c5df0423533b9e976bc9ec7c39c53017Jaikumar Ganesh    }
58327a51be4c5df0423533b9e976bc9ec7c39c53017Jaikumar Ganesh
584b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project    /**
585b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project     * Handlers for incoming service calls
586b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project     */
587b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project    private final IBluetoothHeadset.Stub mBinder = new IBluetoothHeadset.Stub() {
5881d417d7ede71bbcf72a7edebf528efbfcf9b82bbJaikumar Ganesh        public int getConnectionState(BluetoothDevice device) {
589b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project            enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
590b77350c34a4cc1a5ac9d27d20d4c925524bc3ba1Jaikumar Ganesh            BluetoothRemoteHeadset headset = mRemoteHeadsets.get(device);
591b77350c34a4cc1a5ac9d27d20d4c925524bc3ba1Jaikumar Ganesh            if (headset == null) {
5921d417d7ede71bbcf72a7edebf528efbfcf9b82bbJaikumar Ganesh                return BluetoothProfile.STATE_DISCONNECTED;
593b77350c34a4cc1a5ac9d27d20d4c925524bc3ba1Jaikumar Ganesh            }
594b77350c34a4cc1a5ac9d27d20d4c925524bc3ba1Jaikumar Ganesh            return headset.mState;
595b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project        }
596b5d4288b3b4f336601bf6ebf16861d399a4d83a9Jaikumar Ganesh
5977893303ffaf4c2e1dee13fb7cf06525502c03ba1Jaikumar Ganesh        public List<BluetoothDevice> getConnectedDevices() {
598b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project            enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
5991d417d7ede71bbcf72a7edebf528efbfcf9b82bbJaikumar Ganesh            return getDevicesMatchingConnectionStates(
6001d417d7ede71bbcf72a7edebf528efbfcf9b82bbJaikumar Ganesh                new int[] {BluetoothProfile.STATE_CONNECTED});
601b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project        }
602b5d4288b3b4f336601bf6ebf16861d399a4d83a9Jaikumar Ganesh
6031d417d7ede71bbcf72a7edebf528efbfcf9b82bbJaikumar Ganesh        public boolean connect(BluetoothDevice device) {
604b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project            enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
605b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                                           "Need BLUETOOTH_ADMIN permission");
606b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project            synchronized (BluetoothHeadsetService.this) {
6071d417d7ede71bbcf72a7edebf528efbfcf9b82bbJaikumar Ganesh                BluetoothDevice currDevice = getCurrentDevice();
6081d417d7ede71bbcf72a7edebf528efbfcf9b82bbJaikumar Ganesh
6091d417d7ede71bbcf72a7edebf528efbfcf9b82bbJaikumar Ganesh                if (currDevice == device ||
6101d417d7ede71bbcf72a7edebf528efbfcf9b82bbJaikumar Ganesh                    getPriority(device) == BluetoothProfile.PRIORITY_OFF) {
6111d417d7ede71bbcf72a7edebf528efbfcf9b82bbJaikumar Ganesh                    return false;
6121d417d7ede71bbcf72a7edebf528efbfcf9b82bbJaikumar Ganesh                }
6131d417d7ede71bbcf72a7edebf528efbfcf9b82bbJaikumar Ganesh                if (currDevice != null) {
6141d417d7ede71bbcf72a7edebf528efbfcf9b82bbJaikumar Ganesh                    disconnect(currDevice);
6151d417d7ede71bbcf72a7edebf528efbfcf9b82bbJaikumar Ganesh                }
616b77350c34a4cc1a5ac9d27d20d4c925524bc3ba1Jaikumar Ganesh                try {
617b77350c34a4cc1a5ac9d27d20d4c925524bc3ba1Jaikumar Ganesh                    return mBluetoothService.connectHeadset(device.getAddress());
618b77350c34a4cc1a5ac9d27d20d4c925524bc3ba1Jaikumar Ganesh                } catch (RemoteException e) {
619b77350c34a4cc1a5ac9d27d20d4c925524bc3ba1Jaikumar Ganesh                    Log.e(TAG, "connectHeadset");
620b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                    return false;
621b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                }
622b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project            }
623b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project        }
624b5d4288b3b4f336601bf6ebf16861d399a4d83a9Jaikumar Ganesh
6251d417d7ede71bbcf72a7edebf528efbfcf9b82bbJaikumar Ganesh        public boolean disconnect(BluetoothDevice device) {
626b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project            enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
627b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                                           "Need BLUETOOTH_ADMIN permission");
628b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project            synchronized (BluetoothHeadsetService.this) {
6291d417d7ede71bbcf72a7edebf528efbfcf9b82bbJaikumar Ganesh                BluetoothRemoteHeadset headset = mRemoteHeadsets.get(device);
6301d417d7ede71bbcf72a7edebf528efbfcf9b82bbJaikumar Ganesh                if (headset == null ||
6311d417d7ede71bbcf72a7edebf528efbfcf9b82bbJaikumar Ganesh                    headset.mState == BluetoothProfile.STATE_DISCONNECTED ||
6321d417d7ede71bbcf72a7edebf528efbfcf9b82bbJaikumar Ganesh                    headset.mState == BluetoothProfile.STATE_DISCONNECTING) {
6331d417d7ede71bbcf72a7edebf528efbfcf9b82bbJaikumar Ganesh                    return false;
6341d417d7ede71bbcf72a7edebf528efbfcf9b82bbJaikumar Ganesh                }
635b77350c34a4cc1a5ac9d27d20d4c925524bc3ba1Jaikumar Ganesh                try {
6361d417d7ede71bbcf72a7edebf528efbfcf9b82bbJaikumar Ganesh                    return mBluetoothService.disconnectHeadset(device.getAddress());
637b77350c34a4cc1a5ac9d27d20d4c925524bc3ba1Jaikumar Ganesh                } catch (RemoteException e) {
638b77350c34a4cc1a5ac9d27d20d4c925524bc3ba1Jaikumar Ganesh                    Log.e(TAG, "disconnectHeadset");
6391d417d7ede71bbcf72a7edebf528efbfcf9b82bbJaikumar Ganesh                    return false;
640b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                }
641b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project            }
642b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project        }
643b5d4288b3b4f336601bf6ebf16861d399a4d83a9Jaikumar Ganesh
6441d417d7ede71bbcf72a7edebf528efbfcf9b82bbJaikumar Ganesh        public synchronized boolean isAudioConnected(BluetoothDevice device) {
645b5d4288b3b4f336601bf6ebf16861d399a4d83a9Jaikumar Ganesh            enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
646b5d4288b3b4f336601bf6ebf16861d399a4d83a9Jaikumar Ganesh            if (device.equals(mAudioConnectedDevice)) return true;
647b5d4288b3b4f336601bf6ebf16861d399a4d83a9Jaikumar Ganesh            return false;
6481d417d7ede71bbcf72a7edebf528efbfcf9b82bbJaikumar Ganesh        }
649b5d4288b3b4f336601bf6ebf16861d399a4d83a9Jaikumar Ganesh
6507893303ffaf4c2e1dee13fb7cf06525502c03ba1Jaikumar Ganesh        public synchronized List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
651b77350c34a4cc1a5ac9d27d20d4c925524bc3ba1Jaikumar Ganesh            enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
6527893303ffaf4c2e1dee13fb7cf06525502c03ba1Jaikumar Ganesh            List<BluetoothDevice> headsets = new ArrayList<BluetoothDevice>();
6531d417d7ede71bbcf72a7edebf528efbfcf9b82bbJaikumar Ganesh            for (BluetoothDevice device: mRemoteHeadsets.keySet()) {
6541d417d7ede71bbcf72a7edebf528efbfcf9b82bbJaikumar Ganesh                int headsetState = getConnectionState(device);
6551d417d7ede71bbcf72a7edebf528efbfcf9b82bbJaikumar Ganesh                for (int state : states) {
6561d417d7ede71bbcf72a7edebf528efbfcf9b82bbJaikumar Ganesh                    if (state == headsetState) {
6571d417d7ede71bbcf72a7edebf528efbfcf9b82bbJaikumar Ganesh                        headsets.add(device);
6581d417d7ede71bbcf72a7edebf528efbfcf9b82bbJaikumar Ganesh                        break;
6591d417d7ede71bbcf72a7edebf528efbfcf9b82bbJaikumar Ganesh                    }
6601d417d7ede71bbcf72a7edebf528efbfcf9b82bbJaikumar Ganesh                }
6611d417d7ede71bbcf72a7edebf528efbfcf9b82bbJaikumar Ganesh            }
6627893303ffaf4c2e1dee13fb7cf06525502c03ba1Jaikumar Ganesh            return headsets;
663b77350c34a4cc1a5ac9d27d20d4c925524bc3ba1Jaikumar Ganesh        }
664b5d4288b3b4f336601bf6ebf16861d399a4d83a9Jaikumar Ganesh
6651d417d7ede71bbcf72a7edebf528efbfcf9b82bbJaikumar Ganesh        public boolean startVoiceRecognition(BluetoothDevice device) {
666b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project            enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
667b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project            synchronized (BluetoothHeadsetService.this) {
668b77350c34a4cc1a5ac9d27d20d4c925524bc3ba1Jaikumar Ganesh                if (device == null ||
6691d417d7ede71bbcf72a7edebf528efbfcf9b82bbJaikumar Ganesh                    mRemoteHeadsets.get(device) == null ||
6701d417d7ede71bbcf72a7edebf528efbfcf9b82bbJaikumar Ganesh                    mRemoteHeadsets.get(device).mState != BluetoothProfile.STATE_CONNECTED) {
671b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                    return false;
672b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                }
673b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                return mBtHandsfree.startVoiceRecognition();
674b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project            }
675b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project        }
676b5d4288b3b4f336601bf6ebf16861d399a4d83a9Jaikumar Ganesh
6771d417d7ede71bbcf72a7edebf528efbfcf9b82bbJaikumar Ganesh        public boolean stopVoiceRecognition(BluetoothDevice device) {
678b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project            enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
679b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project            synchronized (BluetoothHeadsetService.this) {
680b77350c34a4cc1a5ac9d27d20d4c925524bc3ba1Jaikumar Ganesh                if (device == null ||
6811d417d7ede71bbcf72a7edebf528efbfcf9b82bbJaikumar Ganesh                    mRemoteHeadsets.get(device) == null ||
6821d417d7ede71bbcf72a7edebf528efbfcf9b82bbJaikumar Ganesh                    mRemoteHeadsets.get(device).mState != BluetoothProfile.STATE_CONNECTED) {
683b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                    return false;
684b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                }
685b77350c34a4cc1a5ac9d27d20d4c925524bc3ba1Jaikumar Ganesh
686b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                return mBtHandsfree.stopVoiceRecognition();
687b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project            }
688b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project        }
689b5d4288b3b4f336601bf6ebf16861d399a4d83a9Jaikumar Ganesh
6901d417d7ede71bbcf72a7edebf528efbfcf9b82bbJaikumar Ganesh        public int getBatteryUsageHint(BluetoothDevice device) {
691b77350c34a4cc1a5ac9d27d20d4c925524bc3ba1Jaikumar Ganesh            enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
692b77350c34a4cc1a5ac9d27d20d4c925524bc3ba1Jaikumar Ganesh
693b77350c34a4cc1a5ac9d27d20d4c925524bc3ba1Jaikumar Ganesh            return HeadsetBase.getAtInputCount();
694b77350c34a4cc1a5ac9d27d20d4c925524bc3ba1Jaikumar Ganesh        }
695b5d4288b3b4f336601bf6ebf16861d399a4d83a9Jaikumar Ganesh
696b77350c34a4cc1a5ac9d27d20d4c925524bc3ba1Jaikumar Ganesh        public int getPriority(BluetoothDevice device) {
697b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project            enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
698b77350c34a4cc1a5ac9d27d20d4c925524bc3ba1Jaikumar Ganesh                "Need BLUETOOTH_ADMIN permission");
699b77350c34a4cc1a5ac9d27d20d4c925524bc3ba1Jaikumar Ganesh            synchronized (BluetoothHeadsetService.this) {
700b77350c34a4cc1a5ac9d27d20d4c925524bc3ba1Jaikumar Ganesh                int priority = Settings.Secure.getInt(getContentResolver(),
701b77350c34a4cc1a5ac9d27d20d4c925524bc3ba1Jaikumar Ganesh                        Settings.Secure.getBluetoothHeadsetPriorityKey(device.getAddress()),
7021d417d7ede71bbcf72a7edebf528efbfcf9b82bbJaikumar Ganesh                        BluetoothProfile.PRIORITY_UNDEFINED);
703b77350c34a4cc1a5ac9d27d20d4c925524bc3ba1Jaikumar Ganesh                return priority;
704b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project            }
705b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project        }
706db065e9834bcad9b0cb16c272b4b272dee8bdf62Nick Pelly
707b77350c34a4cc1a5ac9d27d20d4c925524bc3ba1Jaikumar Ganesh        public boolean setPriority(BluetoothDevice device, int priority) {
708b77350c34a4cc1a5ac9d27d20d4c925524bc3ba1Jaikumar Ganesh            enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
709b77350c34a4cc1a5ac9d27d20d4c925524bc3ba1Jaikumar Ganesh                "Need BLUETOOTH_ADMIN permission");
710b77350c34a4cc1a5ac9d27d20d4c925524bc3ba1Jaikumar Ganesh            synchronized (BluetoothHeadsetService.this) {
711b77350c34a4cc1a5ac9d27d20d4c925524bc3ba1Jaikumar Ganesh                Settings.Secure.putInt(getContentResolver(),
712b77350c34a4cc1a5ac9d27d20d4c925524bc3ba1Jaikumar Ganesh                        Settings.Secure.getBluetoothHeadsetPriorityKey(device.getAddress()),
713b77350c34a4cc1a5ac9d27d20d4c925524bc3ba1Jaikumar Ganesh                        priority);
714b77350c34a4cc1a5ac9d27d20d4c925524bc3ba1Jaikumar Ganesh                if (DBG) log("Saved priority " + device + " = " + priority);
715b77350c34a4cc1a5ac9d27d20d4c925524bc3ba1Jaikumar Ganesh                return true;
716b77350c34a4cc1a5ac9d27d20d4c925524bc3ba1Jaikumar Ganesh            }
717b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project        }
718b5d4288b3b4f336601bf6ebf16861d399a4d83a9Jaikumar Ganesh
719b77350c34a4cc1a5ac9d27d20d4c925524bc3ba1Jaikumar Ganesh        public boolean createIncomingConnect(BluetoothDevice device) {
720b77350c34a4cc1a5ac9d27d20d4c925524bc3ba1Jaikumar Ganesh            synchronized (BluetoothHeadsetService.this) {
721b77350c34a4cc1a5ac9d27d20d4c925524bc3ba1Jaikumar Ganesh                HeadsetBase headset;
7221d417d7ede71bbcf72a7edebf528efbfcf9b82bbJaikumar Ganesh                setState(device, BluetoothProfile.STATE_CONNECTING);
723243575cc326b81d558ee0fa3c243e0121fd6f7f6Nick Pelly
724b77350c34a4cc1a5ac9d27d20d4c925524bc3ba1Jaikumar Ganesh                IncomingConnectionInfo info = mRemoteHeadsets.get(device).mIncomingInfo;
725870bd7f09582278afaf7447a3be72653a742b1bdJean-Baptiste Queru                headset = new HeadsetBase(mPowerManager, mAdapter,
726870bd7f09582278afaf7447a3be72653a742b1bdJean-Baptiste Queru                                          device,
727870bd7f09582278afaf7447a3be72653a742b1bdJean-Baptiste Queru                                          info.mSocketFd, info.mRfcommChan,
728870bd7f09582278afaf7447a3be72653a742b1bdJean-Baptiste Queru                                          mConnectedStatusHandler);
729b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project
730b77350c34a4cc1a5ac9d27d20d4c925524bc3ba1Jaikumar Ganesh                mRemoteHeadsets.get(device).mHeadset = headset;
731b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project
732b77350c34a4cc1a5ac9d27d20d4c925524bc3ba1Jaikumar Ganesh                mConnectingStatusHandler.obtainMessage(RFCOMM_CONNECTED, headset).sendToTarget();
733b77350c34a4cc1a5ac9d27d20d4c925524bc3ba1Jaikumar Ganesh                return true;
734b5d4288b3b4f336601bf6ebf16861d399a4d83a9Jaikumar Ganesh            }
735b5d4288b3b4f336601bf6ebf16861d399a4d83a9Jaikumar Ganesh        }
736b5d4288b3b4f336601bf6ebf16861d399a4d83a9Jaikumar Ganesh
737a8b5afa3f830a05fd6679642a6a29dd3265cd273Jaikumar Ganesh        public boolean startScoUsingVirtualVoiceCall(BluetoothDevice device) {
738b5d4288b3b4f336601bf6ebf16861d399a4d83a9Jaikumar Ganesh            enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
739b5d4288b3b4f336601bf6ebf16861d399a4d83a9Jaikumar Ganesh            synchronized (BluetoothHeadsetService.this) {
740b5d4288b3b4f336601bf6ebf16861d399a4d83a9Jaikumar Ganesh                if (device == null ||
741b5d4288b3b4f336601bf6ebf16861d399a4d83a9Jaikumar Ganesh                    mRemoteHeadsets.get(device) == null ||
742a8b5afa3f830a05fd6679642a6a29dd3265cd273Jaikumar Ganesh                    mRemoteHeadsets.get(device).mState != BluetoothProfile.STATE_CONNECTED ||
743a8b5afa3f830a05fd6679642a6a29dd3265cd273Jaikumar Ganesh                    getAudioState(device) != BluetoothHeadset.STATE_AUDIO_DISCONNECTED) {
744b5d4288b3b4f336601bf6ebf16861d399a4d83a9Jaikumar Ganesh                    return false;
745b5d4288b3b4f336601bf6ebf16861d399a4d83a9Jaikumar Ganesh                }
746a8b5afa3f830a05fd6679642a6a29dd3265cd273Jaikumar Ganesh                return mBtHandsfree.initiateScoUsingVirtualVoiceCall();
747b5d4288b3b4f336601bf6ebf16861d399a4d83a9Jaikumar Ganesh            }
748b5d4288b3b4f336601bf6ebf16861d399a4d83a9Jaikumar Ganesh        }
749b5d4288b3b4f336601bf6ebf16861d399a4d83a9Jaikumar Ganesh
750a8b5afa3f830a05fd6679642a6a29dd3265cd273Jaikumar Ganesh        public boolean stopScoUsingVirtualVoiceCall(BluetoothDevice device) {
751b5d4288b3b4f336601bf6ebf16861d399a4d83a9Jaikumar Ganesh            enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
752b5d4288b3b4f336601bf6ebf16861d399a4d83a9Jaikumar Ganesh            synchronized (BluetoothHeadsetService.this) {
753b5d4288b3b4f336601bf6ebf16861d399a4d83a9Jaikumar Ganesh                if (device == null ||
754b5d4288b3b4f336601bf6ebf16861d399a4d83a9Jaikumar Ganesh                    mRemoteHeadsets.get(device) == null ||
755a8b5afa3f830a05fd6679642a6a29dd3265cd273Jaikumar Ganesh                    mRemoteHeadsets.get(device).mState != BluetoothProfile.STATE_CONNECTED ||
756a8b5afa3f830a05fd6679642a6a29dd3265cd273Jaikumar Ganesh                    getAudioState(device) == BluetoothHeadset.STATE_AUDIO_DISCONNECTED) {
757b5d4288b3b4f336601bf6ebf16861d399a4d83a9Jaikumar Ganesh                    return false;
758b5d4288b3b4f336601bf6ebf16861d399a4d83a9Jaikumar Ganesh                }
759a8b5afa3f830a05fd6679642a6a29dd3265cd273Jaikumar Ganesh                return mBtHandsfree.terminateScoUsingVirtualVoiceCall();
760b5d4288b3b4f336601bf6ebf16861d399a4d83a9Jaikumar Ganesh            }
761b5d4288b3b4f336601bf6ebf16861d399a4d83a9Jaikumar Ganesh        }
762b5d4288b3b4f336601bf6ebf16861d399a4d83a9Jaikumar Ganesh
7637d9521b93cadfde1e7fb2e7904641982f1a65bb6Matthew Xie        public boolean rejectIncomingConnect(BluetoothDevice device) {
7647d9521b93cadfde1e7fb2e7904641982f1a65bb6Matthew Xie            synchronized (BluetoothHeadsetService.this) {
7657d9521b93cadfde1e7fb2e7904641982f1a65bb6Matthew Xie                BluetoothRemoteHeadset headset = mRemoteHeadsets.get(device);
7667d9521b93cadfde1e7fb2e7904641982f1a65bb6Matthew Xie                if (headset != null) {
7677d9521b93cadfde1e7fb2e7904641982f1a65bb6Matthew Xie                    IncomingConnectionInfo info = headset.mIncomingInfo;
7687d9521b93cadfde1e7fb2e7904641982f1a65bb6Matthew Xie                    rejectIncomingConnection(info);
7697d9521b93cadfde1e7fb2e7904641982f1a65bb6Matthew Xie                } else {
7707d9521b93cadfde1e7fb2e7904641982f1a65bb6Matthew Xie                    Log.e(TAG, "Error no record of remote headset");
7717d9521b93cadfde1e7fb2e7904641982f1a65bb6Matthew Xie                }
7727d9521b93cadfde1e7fb2e7904641982f1a65bb6Matthew Xie                return true;
7737d9521b93cadfde1e7fb2e7904641982f1a65bb6Matthew Xie            }
7747d9521b93cadfde1e7fb2e7904641982f1a65bb6Matthew Xie        }
7757d9521b93cadfde1e7fb2e7904641982f1a65bb6Matthew Xie
776b77350c34a4cc1a5ac9d27d20d4c925524bc3ba1Jaikumar Ganesh        public boolean acceptIncomingConnect(BluetoothDevice device) {
777b77350c34a4cc1a5ac9d27d20d4c925524bc3ba1Jaikumar Ganesh            synchronized (BluetoothHeadsetService.this) {
778b77350c34a4cc1a5ac9d27d20d4c925524bc3ba1Jaikumar Ganesh                HeadsetBase headset;
779b77350c34a4cc1a5ac9d27d20d4c925524bc3ba1Jaikumar Ganesh                BluetoothRemoteHeadset cachedHeadset = mRemoteHeadsets.get(device);
780b77350c34a4cc1a5ac9d27d20d4c925524bc3ba1Jaikumar Ganesh                if (cachedHeadset == null) {
781b77350c34a4cc1a5ac9d27d20d4c925524bc3ba1Jaikumar Ganesh                    Log.e(TAG, "Cached Headset is Null in acceptIncomingConnect");
782b77350c34a4cc1a5ac9d27d20d4c925524bc3ba1Jaikumar Ganesh                    return false;
783b77350c34a4cc1a5ac9d27d20d4c925524bc3ba1Jaikumar Ganesh                }
784b77350c34a4cc1a5ac9d27d20d4c925524bc3ba1Jaikumar Ganesh                IncomingConnectionInfo info = cachedHeadset.mIncomingInfo;
785870bd7f09582278afaf7447a3be72653a742b1bdJean-Baptiste Queru                headset = new HeadsetBase(mPowerManager, mAdapter,
786870bd7f09582278afaf7447a3be72653a742b1bdJean-Baptiste Queru                                          device,
787870bd7f09582278afaf7447a3be72653a742b1bdJean-Baptiste Queru                                          info.mSocketFd, info.mRfcommChan,
788870bd7f09582278afaf7447a3be72653a742b1bdJean-Baptiste Queru                                          mConnectedStatusHandler);
789b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project
7901d417d7ede71bbcf72a7edebf528efbfcf9b82bbJaikumar Ganesh                setState(device, BluetoothProfile.STATE_CONNECTED);
791b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project
792b77350c34a4cc1a5ac9d27d20d4c925524bc3ba1Jaikumar Ganesh                cachedHeadset.mHeadset = headset;
793b77350c34a4cc1a5ac9d27d20d4c925524bc3ba1Jaikumar Ganesh                mBtHandsfree.connectHeadset(headset, cachedHeadset.mHeadsetType);
794b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project
795b77350c34a4cc1a5ac9d27d20d4c925524bc3ba1Jaikumar Ganesh                if (DBG) log("Successfully used incoming connection");
796b77350c34a4cc1a5ac9d27d20d4c925524bc3ba1Jaikumar Ganesh                return true;
797b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project            }
798b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project        }
799b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project
800b77350c34a4cc1a5ac9d27d20d4c925524bc3ba1Jaikumar Ganesh        public  boolean cancelConnectThread() {
801b77350c34a4cc1a5ac9d27d20d4c925524bc3ba1Jaikumar Ganesh            synchronized (BluetoothHeadsetService.this) {
802b77350c34a4cc1a5ac9d27d20d4c925524bc3ba1Jaikumar Ganesh                if (mConnectThread != null) {
803b77350c34a4cc1a5ac9d27d20d4c925524bc3ba1Jaikumar Ganesh                    // cancel the connection thread
804b77350c34a4cc1a5ac9d27d20d4c925524bc3ba1Jaikumar Ganesh                    mConnectThread.interrupt();
805b77350c34a4cc1a5ac9d27d20d4c925524bc3ba1Jaikumar Ganesh                    try {
806b77350c34a4cc1a5ac9d27d20d4c925524bc3ba1Jaikumar Ganesh                        mConnectThread.join();
807b77350c34a4cc1a5ac9d27d20d4c925524bc3ba1Jaikumar Ganesh                    } catch (InterruptedException e) {
808b77350c34a4cc1a5ac9d27d20d4c925524bc3ba1Jaikumar Ganesh                        Log.e(TAG, "Connection cancelled twice?", e);
809b77350c34a4cc1a5ac9d27d20d4c925524bc3ba1Jaikumar Ganesh                    }
810b77350c34a4cc1a5ac9d27d20d4c925524bc3ba1Jaikumar Ganesh                    mConnectThread = null;
811b77350c34a4cc1a5ac9d27d20d4c925524bc3ba1Jaikumar Ganesh                }
812b77350c34a4cc1a5ac9d27d20d4c925524bc3ba1Jaikumar Ganesh                return true;
813b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project            }
814b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project        }
815b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project
816b77350c34a4cc1a5ac9d27d20d4c925524bc3ba1Jaikumar Ganesh        public boolean connectHeadsetInternal(BluetoothDevice device) {
817b77350c34a4cc1a5ac9d27d20d4c925524bc3ba1Jaikumar Ganesh            synchronized (BluetoothHeadsetService.this) {
818b77350c34a4cc1a5ac9d27d20d4c925524bc3ba1Jaikumar Ganesh                BluetoothDevice currDevice = getCurrentDevice();
819b77350c34a4cc1a5ac9d27d20d4c925524bc3ba1Jaikumar Ganesh                if (currDevice == null) {
820b77350c34a4cc1a5ac9d27d20d4c925524bc3ba1Jaikumar Ganesh                    BluetoothRemoteHeadset headset = new BluetoothRemoteHeadset();
821b77350c34a4cc1a5ac9d27d20d4c925524bc3ba1Jaikumar Ganesh                    mRemoteHeadsets.put(device, headset);
822b77350c34a4cc1a5ac9d27d20d4c925524bc3ba1Jaikumar Ganesh
8231d417d7ede71bbcf72a7edebf528efbfcf9b82bbJaikumar Ganesh                    setState(device, BluetoothProfile.STATE_CONNECTING);
824b77350c34a4cc1a5ac9d27d20d4c925524bc3ba1Jaikumar Ganesh                    if (device.getUuids() == null) {
825b77350c34a4cc1a5ac9d27d20d4c925524bc3ba1Jaikumar Ganesh                        // We might not have got the UUID change notification from
826b77350c34a4cc1a5ac9d27d20d4c925524bc3ba1Jaikumar Ganesh                        // Bluez yet, if we have just paired. Try after 1.5 secs.
827b77350c34a4cc1a5ac9d27d20d4c925524bc3ba1Jaikumar Ganesh                        Message msg = new Message();
828b77350c34a4cc1a5ac9d27d20d4c925524bc3ba1Jaikumar Ganesh                        msg.what = CONNECT_HEADSET_DELAYED;
829b77350c34a4cc1a5ac9d27d20d4c925524bc3ba1Jaikumar Ganesh                        msg.obj = device;
830b77350c34a4cc1a5ac9d27d20d4c925524bc3ba1Jaikumar Ganesh                        mHandler.sendMessageDelayed(msg, 1500);
831b77350c34a4cc1a5ac9d27d20d4c925524bc3ba1Jaikumar Ganesh                    } else {
832b77350c34a4cc1a5ac9d27d20d4c925524bc3ba1Jaikumar Ganesh                        getSdpRecordsAndConnect(device);
833b77350c34a4cc1a5ac9d27d20d4c925524bc3ba1Jaikumar Ganesh                    }
834b77350c34a4cc1a5ac9d27d20d4c925524bc3ba1Jaikumar Ganesh                    return true;
835b77350c34a4cc1a5ac9d27d20d4c925524bc3ba1Jaikumar Ganesh                } else {
836b77350c34a4cc1a5ac9d27d20d4c925524bc3ba1Jaikumar Ganesh                      Log.w(TAG, "connectHeadset(" + device + "): failed: already in state " +
837b77350c34a4cc1a5ac9d27d20d4c925524bc3ba1Jaikumar Ganesh                            mRemoteHeadsets.get(currDevice).mState +
838b77350c34a4cc1a5ac9d27d20d4c925524bc3ba1Jaikumar Ganesh                            " with headset " + currDevice);
839b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                }
840b77350c34a4cc1a5ac9d27d20d4c925524bc3ba1Jaikumar Ganesh                return false;
841b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project            }
842b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project        }
843b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project
844b77350c34a4cc1a5ac9d27d20d4c925524bc3ba1Jaikumar Ganesh        public boolean disconnectHeadsetInternal(BluetoothDevice device) {
845b77350c34a4cc1a5ac9d27d20d4c925524bc3ba1Jaikumar Ganesh            synchronized (BluetoothHeadsetService.this) {
846b77350c34a4cc1a5ac9d27d20d4c925524bc3ba1Jaikumar Ganesh                BluetoothRemoteHeadset remoteHeadset = mRemoteHeadsets.get(device);
847b77350c34a4cc1a5ac9d27d20d4c925524bc3ba1Jaikumar Ganesh                if (remoteHeadset == null) return false;
848b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project
8491d417d7ede71bbcf72a7edebf528efbfcf9b82bbJaikumar Ganesh                if (remoteHeadset.mState == BluetoothProfile.STATE_CONNECTED) {
850b77350c34a4cc1a5ac9d27d20d4c925524bc3ba1Jaikumar Ganesh                    // Send a dummy battery level message to force headset
851b77350c34a4cc1a5ac9d27d20d4c925524bc3ba1Jaikumar Ganesh                    // out of sniff mode so that it will immediately notice
852b77350c34a4cc1a5ac9d27d20d4c925524bc3ba1Jaikumar Ganesh                    // the disconnection. We are currently sending it for
853b77350c34a4cc1a5ac9d27d20d4c925524bc3ba1Jaikumar Ganesh                    // handsfree only.
854b77350c34a4cc1a5ac9d27d20d4c925524bc3ba1Jaikumar Ganesh                    // TODO: Call hci_conn_enter_active_mode() from
855b77350c34a4cc1a5ac9d27d20d4c925524bc3ba1Jaikumar Ganesh                    // rfcomm_send_disc() in the kernel instead.
856b77350c34a4cc1a5ac9d27d20d4c925524bc3ba1Jaikumar Ganesh                    // See http://b/1716887
8571d417d7ede71bbcf72a7edebf528efbfcf9b82bbJaikumar Ganesh                    setState(device, BluetoothProfile.STATE_DISCONNECTING);
8581d417d7ede71bbcf72a7edebf528efbfcf9b82bbJaikumar Ganesh
859b77350c34a4cc1a5ac9d27d20d4c925524bc3ba1Jaikumar Ganesh                    HeadsetBase headset = remoteHeadset.mHeadset;
860b77350c34a4cc1a5ac9d27d20d4c925524bc3ba1Jaikumar Ganesh                    if (remoteHeadset.mHeadsetType == BluetoothHandsfree.TYPE_HANDSFREE) {
861b77350c34a4cc1a5ac9d27d20d4c925524bc3ba1Jaikumar Ganesh                        headset.sendURC("+CIEV: 7,3");
862b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                    }
863b77350c34a4cc1a5ac9d27d20d4c925524bc3ba1Jaikumar Ganesh
864b77350c34a4cc1a5ac9d27d20d4c925524bc3ba1Jaikumar Ganesh                    if (headset != null) {
865b77350c34a4cc1a5ac9d27d20d4c925524bc3ba1Jaikumar Ganesh                        headset.disconnect();
866b77350c34a4cc1a5ac9d27d20d4c925524bc3ba1Jaikumar Ganesh                        headset = null;
867b77350c34a4cc1a5ac9d27d20d4c925524bc3ba1Jaikumar Ganesh                    }
8681d417d7ede71bbcf72a7edebf528efbfcf9b82bbJaikumar Ganesh                    setState(device, BluetoothProfile.STATE_DISCONNECTED);
869b77350c34a4cc1a5ac9d27d20d4c925524bc3ba1Jaikumar Ganesh                    return true;
8701d417d7ede71bbcf72a7edebf528efbfcf9b82bbJaikumar Ganesh                } else if (remoteHeadset.mState == BluetoothProfile.STATE_CONNECTING) {
871b77350c34a4cc1a5ac9d27d20d4c925524bc3ba1Jaikumar Ganesh                    // The state machine would have canceled the connect thread.
872b77350c34a4cc1a5ac9d27d20d4c925524bc3ba1Jaikumar Ganesh                    // Just set the state here.
8731d417d7ede71bbcf72a7edebf528efbfcf9b82bbJaikumar Ganesh                    setState(device, BluetoothProfile.STATE_DISCONNECTED);
874b77350c34a4cc1a5ac9d27d20d4c925524bc3ba1Jaikumar Ganesh                    return true;
875b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                }
876b77350c34a4cc1a5ac9d27d20d4c925524bc3ba1Jaikumar Ganesh                return false;
877b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project            }
878b77350c34a4cc1a5ac9d27d20d4c925524bc3ba1Jaikumar Ganesh        }
879b5d4288b3b4f336601bf6ebf16861d399a4d83a9Jaikumar Ganesh
8801d417d7ede71bbcf72a7edebf528efbfcf9b82bbJaikumar Ganesh        public boolean setAudioState(BluetoothDevice device, int state) {
881d9cd25a4dde8ec303893815d0a6a61c824c0bc5cMatthew Xie            // mRemoteHeadsets handles put/get concurrency by itself
882d9cd25a4dde8ec303893815d0a6a61c824c0bc5cMatthew Xie            int prevState = mRemoteHeadsets.get(device).mAudioState;
883d9cd25a4dde8ec303893815d0a6a61c824c0bc5cMatthew Xie            mRemoteHeadsets.get(device).mAudioState = state;
884d9cd25a4dde8ec303893815d0a6a61c824c0bc5cMatthew Xie            if (state == BluetoothHeadset.STATE_AUDIO_CONNECTED) {
885d9cd25a4dde8ec303893815d0a6a61c824c0bc5cMatthew Xie                mAudioConnectedDevice = device;
886d9cd25a4dde8ec303893815d0a6a61c824c0bc5cMatthew Xie            } else if (state == BluetoothHeadset.STATE_AUDIO_DISCONNECTED) {
887d9cd25a4dde8ec303893815d0a6a61c824c0bc5cMatthew Xie                mAudioConnectedDevice = null;
888d9cd25a4dde8ec303893815d0a6a61c824c0bc5cMatthew Xie            }
889d9cd25a4dde8ec303893815d0a6a61c824c0bc5cMatthew Xie            Intent intent = new Intent(BluetoothHeadset.ACTION_AUDIO_STATE_CHANGED);
890d9cd25a4dde8ec303893815d0a6a61c824c0bc5cMatthew Xie            intent.putExtra(BluetoothHeadset.EXTRA_STATE, state);
891d9cd25a4dde8ec303893815d0a6a61c824c0bc5cMatthew Xie            intent.putExtra(BluetoothHeadset.EXTRA_PREVIOUS_STATE, prevState);
892d9cd25a4dde8ec303893815d0a6a61c824c0bc5cMatthew Xie            intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device);
893d9cd25a4dde8ec303893815d0a6a61c824c0bc5cMatthew Xie            sendBroadcast(intent, android.Manifest.permission.BLUETOOTH);
894d9cd25a4dde8ec303893815d0a6a61c824c0bc5cMatthew Xie            if (DBG) log("AudioStateIntent: " + device + " State: " + state
895d9cd25a4dde8ec303893815d0a6a61c824c0bc5cMatthew Xie                         + " PrevState: " + prevState);
896d9cd25a4dde8ec303893815d0a6a61c824c0bc5cMatthew Xie            return true;
8971d417d7ede71bbcf72a7edebf528efbfcf9b82bbJaikumar Ganesh        }
898912186b04d22881fd025a7cbe586228b85721a05Jaikumar Ganesh
899912186b04d22881fd025a7cbe586228b85721a05Jaikumar Ganesh        public int getAudioState(BluetoothDevice device) {
900d9cd25a4dde8ec303893815d0a6a61c824c0bc5cMatthew Xie            // mRemoteHeadsets handles put/get concurrency by itself
901d9cd25a4dde8ec303893815d0a6a61c824c0bc5cMatthew Xie            BluetoothRemoteHeadset headset = mRemoteHeadsets.get(device);
902d9cd25a4dde8ec303893815d0a6a61c824c0bc5cMatthew Xie            if (headset == null) return BluetoothHeadset.STATE_AUDIO_DISCONNECTED;
903912186b04d22881fd025a7cbe586228b85721a05Jaikumar Ganesh
904d9cd25a4dde8ec303893815d0a6a61c824c0bc5cMatthew Xie            return headset.mAudioState;
905912186b04d22881fd025a7cbe586228b85721a05Jaikumar Ganesh       }
906b77350c34a4cc1a5ac9d27d20d4c925524bc3ba1Jaikumar Ganesh    };
907b77350c34a4cc1a5ac9d27d20d4c925524bc3ba1Jaikumar Ganesh
908b77350c34a4cc1a5ac9d27d20d4c925524bc3ba1Jaikumar Ganesh    @Override
909b77350c34a4cc1a5ac9d27d20d4c925524bc3ba1Jaikumar Ganesh    public void onDestroy() {
910b77350c34a4cc1a5ac9d27d20d4c925524bc3ba1Jaikumar Ganesh        super.onDestroy();
911b77350c34a4cc1a5ac9d27d20d4c925524bc3ba1Jaikumar Ganesh        if (DBG) log("Stopping BluetoothHeadsetService");
912b77350c34a4cc1a5ac9d27d20d4c925524bc3ba1Jaikumar Ganesh        unregisterReceiver(mBluetoothReceiver);
913b77350c34a4cc1a5ac9d27d20d4c925524bc3ba1Jaikumar Ganesh        mBtHandsfree.onBluetoothDisabled();
914b77350c34a4cc1a5ac9d27d20d4c925524bc3ba1Jaikumar Ganesh        mAg.stop();
915b77350c34a4cc1a5ac9d27d20d4c925524bc3ba1Jaikumar Ganesh        sHasStarted = false;
9161d417d7ede71bbcf72a7edebf528efbfcf9b82bbJaikumar Ganesh        BluetoothDevice device = getCurrentDevice();
9171d417d7ede71bbcf72a7edebf528efbfcf9b82bbJaikumar Ganesh        if (device != null) {
9181d417d7ede71bbcf72a7edebf528efbfcf9b82bbJaikumar Ganesh            setState(device, BluetoothProfile.STATE_DISCONNECTED);
919b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project        }
920b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project    }
921b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project
922b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project
923b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project
924b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project    private static void log(String msg) {
925b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project        Log.d(TAG, msg);
926b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project    }
927b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project}
928