16c91bc0a163cc7600c40d7fb979777fd911d1ef1Matthew Xie/*
2ede67c26e7b2564ea35db6d9b3027a269c150e13Zhihai Xu * Copyright (C) 2012 The Android Open Source Project
3ede67c26e7b2564ea35db6d9b3027a269c150e13Zhihai Xu *
4ede67c26e7b2564ea35db6d9b3027a269c150e13Zhihai Xu * Licensed under the Apache License, Version 2.0 (the "License");
5ede67c26e7b2564ea35db6d9b3027a269c150e13Zhihai Xu * you may not use this file except in compliance with the License.
6ede67c26e7b2564ea35db6d9b3027a269c150e13Zhihai Xu * You may obtain a copy of the License at
7ede67c26e7b2564ea35db6d9b3027a269c150e13Zhihai Xu *
8ede67c26e7b2564ea35db6d9b3027a269c150e13Zhihai Xu *      http://www.apache.org/licenses/LICENSE-2.0
9ede67c26e7b2564ea35db6d9b3027a269c150e13Zhihai Xu *
10ede67c26e7b2564ea35db6d9b3027a269c150e13Zhihai Xu * Unless required by applicable law or agreed to in writing, software
11ede67c26e7b2564ea35db6d9b3027a269c150e13Zhihai Xu * distributed under the License is distributed on an "AS IS" BASIS,
12ede67c26e7b2564ea35db6d9b3027a269c150e13Zhihai Xu * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13ede67c26e7b2564ea35db6d9b3027a269c150e13Zhihai Xu * See the License for the specific language governing permissions and
14ede67c26e7b2564ea35db6d9b3027a269c150e13Zhihai Xu * limitations under the License.
156c91bc0a163cc7600c40d7fb979777fd911d1ef1Matthew Xie */
166c91bc0a163cc7600c40d7fb979777fd911d1ef1Matthew Xie
176c91bc0a163cc7600c40d7fb979777fd911d1ef1Matthew Xiepackage com.android.bluetooth.hfp;
186c91bc0a163cc7600c40d7fb979777fd911d1ef1Matthew Xie
196c91bc0a163cc7600c40d7fb979777fd911d1ef1Matthew Xieimport android.bluetooth.BluetoothDevice;
2074ae04c73312403e89db0f8e9bd9601d403b4783fredcimport android.bluetooth.BluetoothHeadset;
216c91bc0a163cc7600c40d7fb979777fd911d1ef1Matthew Xieimport android.bluetooth.BluetoothProfile;
226c91bc0a163cc7600c40d7fb979777fd911d1ef1Matthew Xieimport android.bluetooth.IBluetoothHeadset;
236c91bc0a163cc7600c40d7fb979777fd911d1ef1Matthew Xieimport android.content.BroadcastReceiver;
246c91bc0a163cc7600c40d7fb979777fd911d1ef1Matthew Xieimport android.content.Context;
256c91bc0a163cc7600c40d7fb979777fd911d1ef1Matthew Xieimport android.content.Intent;
266c91bc0a163cc7600c40d7fb979777fd911d1ef1Matthew Xieimport android.content.IntentFilter;
2737510150ee738ccfa5913f7120e28f25ae7768dfZhihai Xuimport android.content.pm.PackageManager;
286c91bc0a163cc7600c40d7fb979777fd911d1ef1Matthew Xieimport android.media.AudioManager;
296c91bc0a163cc7600c40d7fb979777fd911d1ef1Matthew Xieimport android.os.Handler;
306c91bc0a163cc7600c40d7fb979777fd911d1ef1Matthew Xieimport android.os.Message;
316c91bc0a163cc7600c40d7fb979777fd911d1ef1Matthew Xieimport android.provider.Settings;
326c91bc0a163cc7600c40d7fb979777fd911d1ef1Matthew Xieimport android.util.Log;
3337510150ee738ccfa5913f7120e28f25ae7768dfZhihai Xuimport com.android.bluetooth.btservice.ProfileService;
3437510150ee738ccfa5913f7120e28f25ae7768dfZhihai Xuimport com.android.bluetooth.Utils;
3574ae04c73312403e89db0f8e9bd9601d403b4783fredcimport java.util.ArrayList;
366c91bc0a163cc7600c40d7fb979777fd911d1ef1Matthew Xieimport java.util.List;
376654f5c903de510a70f9e72cd5ad7837b615d93ffredcimport java.util.Iterator;
386654f5c903de510a70f9e72cd5ad7837b615d93ffredcimport java.util.Map;
396c91bc0a163cc7600c40d7fb979777fd911d1ef1Matthew Xie
406c91bc0a163cc7600c40d7fb979777fd911d1ef1Matthew Xie/**
416c91bc0a163cc7600c40d7fb979777fd911d1ef1Matthew Xie * Provides Bluetooth Headset and Handsfree profile, as a service in
426c91bc0a163cc7600c40d7fb979777fd911d1ef1Matthew Xie * the Bluetooth application.
436c91bc0a163cc7600c40d7fb979777fd911d1ef1Matthew Xie * @hide
446c91bc0a163cc7600c40d7fb979777fd911d1ef1Matthew Xie */
45b5cc776c9353a203cdde97e62b25f05d9633d14cfredcpublic class HeadsetService extends ProfileService {
46fd1da115cbf09b7dd9bca3c7d3a4fb816a835dc5Matthew Xie    private static final boolean DBG = false;
47b5cc776c9353a203cdde97e62b25f05d9633d14cfredc    private static final String TAG = "HeadsetService";
480e46362107a23fcfe829b270f3ff987d911cd102Matthew Xie    private static final String MODIFY_PHONE_STATE = android.Manifest.permission.MODIFY_PHONE_STATE;
490e46362107a23fcfe829b270f3ff987d911cd102Matthew Xie
506c91bc0a163cc7600c40d7fb979777fd911d1ef1Matthew Xie    private HeadsetStateMachine mStateMachine;
514f5430babbc5a8f870e5a578a4ea3452f41dd97aGanesh Ganapathi Batta    private static HeadsetService sHeadsetService;
526c91bc0a163cc7600c40d7fb979777fd911d1ef1Matthew Xie
53b5cc776c9353a203cdde97e62b25f05d9633d14cfredc    protected String getName() {
54b5cc776c9353a203cdde97e62b25f05d9633d14cfredc        return TAG;
556c91bc0a163cc7600c40d7fb979777fd911d1ef1Matthew Xie    }
566c91bc0a163cc7600c40d7fb979777fd911d1ef1Matthew Xie
5774ae04c73312403e89db0f8e9bd9601d403b4783fredc    public IProfileServiceBinder initBinder() {
5874ae04c73312403e89db0f8e9bd9601d403b4783fredc        return new BluetoothHeadsetBinder(this);
596c91bc0a163cc7600c40d7fb979777fd911d1ef1Matthew Xie    }
606c91bc0a163cc7600c40d7fb979777fd911d1ef1Matthew Xie
61b5cc776c9353a203cdde97e62b25f05d9633d14cfredc    protected boolean start() {
626893668d1ccf0cc6361ef31ace52fedc2e37e35aWink Saville        mStateMachine = HeadsetStateMachine.make(this);
636654f5c903de510a70f9e72cd5ad7837b615d93ffredc        IntentFilter filter = new IntentFilter(Intent.ACTION_BATTERY_CHANGED);
646654f5c903de510a70f9e72cd5ad7837b615d93ffredc        filter.addAction(AudioManager.VOLUME_CHANGED_ACTION);
65405b6148a168c05f9c9eac6a80db68b9a58392e2Sreenidhi T        filter.addAction(BluetoothDevice.ACTION_CONNECTION_ACCESS_REPLY);
6674ae04c73312403e89db0f8e9bd9601d403b4783fredc        try {
6774ae04c73312403e89db0f8e9bd9601d403b4783fredc            registerReceiver(mHeadsetReceiver, filter);
6874ae04c73312403e89db0f8e9bd9601d403b4783fredc        } catch (Exception e) {
6974ae04c73312403e89db0f8e9bd9601d403b4783fredc            Log.w(TAG,"Unable to register headset receiver",e);
7074ae04c73312403e89db0f8e9bd9601d403b4783fredc        }
714f5430babbc5a8f870e5a578a4ea3452f41dd97aGanesh Ganapathi Batta        setHeadsetService(this);
72b5cc776c9353a203cdde97e62b25f05d9633d14cfredc        return true;
736654f5c903de510a70f9e72cd5ad7837b615d93ffredc    }
746654f5c903de510a70f9e72cd5ad7837b615d93ffredc
75b5cc776c9353a203cdde97e62b25f05d9633d14cfredc    protected boolean stop() {
7674ae04c73312403e89db0f8e9bd9601d403b4783fredc        try {
7774ae04c73312403e89db0f8e9bd9601d403b4783fredc            unregisterReceiver(mHeadsetReceiver);
7874ae04c73312403e89db0f8e9bd9601d403b4783fredc        } catch (Exception e) {
7974ae04c73312403e89db0f8e9bd9601d403b4783fredc            Log.w(TAG,"Unable to unregister headset receiver",e);
80b5cc776c9353a203cdde97e62b25f05d9633d14cfredc        }
8115d36984a79d6e35c659edb0efdf929f0b526bd5Fred        mStateMachine.doQuit();
8274ae04c73312403e89db0f8e9bd9601d403b4783fredc        return true;
8374ae04c73312403e89db0f8e9bd9601d403b4783fredc    }
846654f5c903de510a70f9e72cd5ad7837b615d93ffredc
8574ae04c73312403e89db0f8e9bd9601d403b4783fredc    protected boolean cleanup() {
8674ae04c73312403e89db0f8e9bd9601d403b4783fredc        if (mStateMachine != null) {
876654f5c903de510a70f9e72cd5ad7837b615d93ffredc            mStateMachine.cleanup();
886654f5c903de510a70f9e72cd5ad7837b615d93ffredc        }
894f5430babbc5a8f870e5a578a4ea3452f41dd97aGanesh Ganapathi Batta        clearHeadsetService();
90b5cc776c9353a203cdde97e62b25f05d9633d14cfredc        return true;
916c91bc0a163cc7600c40d7fb979777fd911d1ef1Matthew Xie    }
926c91bc0a163cc7600c40d7fb979777fd911d1ef1Matthew Xie
936c91bc0a163cc7600c40d7fb979777fd911d1ef1Matthew Xie    private final BroadcastReceiver mHeadsetReceiver = new BroadcastReceiver() {
946c91bc0a163cc7600c40d7fb979777fd911d1ef1Matthew Xie        @Override
956c91bc0a163cc7600c40d7fb979777fd911d1ef1Matthew Xie        public void onReceive(Context context, Intent intent) {
966c91bc0a163cc7600c40d7fb979777fd911d1ef1Matthew Xie            String action = intent.getAction();
976c91bc0a163cc7600c40d7fb979777fd911d1ef1Matthew Xie            if (action.equals(Intent.ACTION_BATTERY_CHANGED)) {
986c91bc0a163cc7600c40d7fb979777fd911d1ef1Matthew Xie                mStateMachine.sendMessage(HeadsetStateMachine.INTENT_BATTERY_CHANGED, intent);
996c91bc0a163cc7600c40d7fb979777fd911d1ef1Matthew Xie            } else if (action.equals(AudioManager.VOLUME_CHANGED_ACTION)) {
1006c91bc0a163cc7600c40d7fb979777fd911d1ef1Matthew Xie                int streamType = intent.getIntExtra(AudioManager.EXTRA_VOLUME_STREAM_TYPE, -1);
1016c91bc0a163cc7600c40d7fb979777fd911d1ef1Matthew Xie                if (streamType == AudioManager.STREAM_BLUETOOTH_SCO) {
1026c91bc0a163cc7600c40d7fb979777fd911d1ef1Matthew Xie                    mStateMachine.sendMessage(HeadsetStateMachine.INTENT_SCO_VOLUME_CHANGED,
1036c91bc0a163cc7600c40d7fb979777fd911d1ef1Matthew Xie                                              intent);
1046c91bc0a163cc7600c40d7fb979777fd911d1ef1Matthew Xie                }
1056c91bc0a163cc7600c40d7fb979777fd911d1ef1Matthew Xie            }
106405b6148a168c05f9c9eac6a80db68b9a58392e2Sreenidhi T            else if (action.equals(BluetoothDevice.ACTION_CONNECTION_ACCESS_REPLY)) {
107405b6148a168c05f9c9eac6a80db68b9a58392e2Sreenidhi T                Log.v(TAG, "HeadsetService -  Received BluetoothDevice.ACTION_CONNECTION_ACCESS_REPLY");
108405b6148a168c05f9c9eac6a80db68b9a58392e2Sreenidhi T                mStateMachine.handleAccessPermissionResult(intent);
109405b6148a168c05f9c9eac6a80db68b9a58392e2Sreenidhi T            }
1106c91bc0a163cc7600c40d7fb979777fd911d1ef1Matthew Xie        }
1116c91bc0a163cc7600c40d7fb979777fd911d1ef1Matthew Xie    };
1126c91bc0a163cc7600c40d7fb979777fd911d1ef1Matthew Xie
1136c91bc0a163cc7600c40d7fb979777fd911d1ef1Matthew Xie    /**
1146c91bc0a163cc7600c40d7fb979777fd911d1ef1Matthew Xie     * Handlers for incoming service calls
1156c91bc0a163cc7600c40d7fb979777fd911d1ef1Matthew Xie     */
11674ae04c73312403e89db0f8e9bd9601d403b4783fredc    private static class BluetoothHeadsetBinder extends IBluetoothHeadset.Stub implements IProfileServiceBinder {
11774ae04c73312403e89db0f8e9bd9601d403b4783fredc        private HeadsetService mService;
1186c91bc0a163cc7600c40d7fb979777fd911d1ef1Matthew Xie
11974ae04c73312403e89db0f8e9bd9601d403b4783fredc        public BluetoothHeadsetBinder(HeadsetService svc) {
12074ae04c73312403e89db0f8e9bd9601d403b4783fredc            mService = svc;
12174ae04c73312403e89db0f8e9bd9601d403b4783fredc        }
12274ae04c73312403e89db0f8e9bd9601d403b4783fredc        public boolean cleanup() {
12374ae04c73312403e89db0f8e9bd9601d403b4783fredc            mService = null;
12474ae04c73312403e89db0f8e9bd9601d403b4783fredc            return true;
12574ae04c73312403e89db0f8e9bd9601d403b4783fredc        }
1266c91bc0a163cc7600c40d7fb979777fd911d1ef1Matthew Xie
12774ae04c73312403e89db0f8e9bd9601d403b4783fredc        private HeadsetService getService() {
12837510150ee738ccfa5913f7120e28f25ae7768dfZhihai Xu            if (!Utils.checkCaller()) {
12937510150ee738ccfa5913f7120e28f25ae7768dfZhihai Xu                Log.w(TAG,"Headset call not allowed for non-active user");
13037510150ee738ccfa5913f7120e28f25ae7768dfZhihai Xu                return null;
13137510150ee738ccfa5913f7120e28f25ae7768dfZhihai Xu            }
13237510150ee738ccfa5913f7120e28f25ae7768dfZhihai Xu
13374ae04c73312403e89db0f8e9bd9601d403b4783fredc            if (mService  != null && mService.isAvailable()) {
13474ae04c73312403e89db0f8e9bd9601d403b4783fredc                return mService;
1356c91bc0a163cc7600c40d7fb979777fd911d1ef1Matthew Xie            }
13674ae04c73312403e89db0f8e9bd9601d403b4783fredc            return null;
13774ae04c73312403e89db0f8e9bd9601d403b4783fredc        }
1386c91bc0a163cc7600c40d7fb979777fd911d1ef1Matthew Xie
13974ae04c73312403e89db0f8e9bd9601d403b4783fredc        public boolean connect(BluetoothDevice device) {
14074ae04c73312403e89db0f8e9bd9601d403b4783fredc            HeadsetService service = getService();
14174ae04c73312403e89db0f8e9bd9601d403b4783fredc            if (service == null) return false;
14274ae04c73312403e89db0f8e9bd9601d403b4783fredc            return service.connect(device);
1436c91bc0a163cc7600c40d7fb979777fd911d1ef1Matthew Xie        }
1446c91bc0a163cc7600c40d7fb979777fd911d1ef1Matthew Xie
1456c91bc0a163cc7600c40d7fb979777fd911d1ef1Matthew Xie        public boolean disconnect(BluetoothDevice device) {
14674ae04c73312403e89db0f8e9bd9601d403b4783fredc            HeadsetService service = getService();
14774ae04c73312403e89db0f8e9bd9601d403b4783fredc            if (service == null) return false;
14874ae04c73312403e89db0f8e9bd9601d403b4783fredc            return service.disconnect(device);
1496c91bc0a163cc7600c40d7fb979777fd911d1ef1Matthew Xie        }
1506c91bc0a163cc7600c40d7fb979777fd911d1ef1Matthew Xie
1516c91bc0a163cc7600c40d7fb979777fd911d1ef1Matthew Xie        public List<BluetoothDevice> getConnectedDevices() {
15274ae04c73312403e89db0f8e9bd9601d403b4783fredc            HeadsetService service = getService();
15374ae04c73312403e89db0f8e9bd9601d403b4783fredc            if (service == null) return new ArrayList<BluetoothDevice>(0);
15474ae04c73312403e89db0f8e9bd9601d403b4783fredc            return service.getConnectedDevices();
1556c91bc0a163cc7600c40d7fb979777fd911d1ef1Matthew Xie        }
1566c91bc0a163cc7600c40d7fb979777fd911d1ef1Matthew Xie
1576c91bc0a163cc7600c40d7fb979777fd911d1ef1Matthew Xie        public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
15874ae04c73312403e89db0f8e9bd9601d403b4783fredc            HeadsetService service = getService();
15974ae04c73312403e89db0f8e9bd9601d403b4783fredc            if (service == null) return new ArrayList<BluetoothDevice>(0);
16074ae04c73312403e89db0f8e9bd9601d403b4783fredc            return service.getDevicesMatchingConnectionStates(states);
1616c91bc0a163cc7600c40d7fb979777fd911d1ef1Matthew Xie        }
1626c91bc0a163cc7600c40d7fb979777fd911d1ef1Matthew Xie
1636c91bc0a163cc7600c40d7fb979777fd911d1ef1Matthew Xie        public int getConnectionState(BluetoothDevice device) {
16474ae04c73312403e89db0f8e9bd9601d403b4783fredc            HeadsetService service = getService();
16574ae04c73312403e89db0f8e9bd9601d403b4783fredc            if (service == null) return BluetoothProfile.STATE_DISCONNECTED;
16674ae04c73312403e89db0f8e9bd9601d403b4783fredc            return service.getConnectionState(device);
1676c91bc0a163cc7600c40d7fb979777fd911d1ef1Matthew Xie        }
1686c91bc0a163cc7600c40d7fb979777fd911d1ef1Matthew Xie
1696c91bc0a163cc7600c40d7fb979777fd911d1ef1Matthew Xie        public boolean setPriority(BluetoothDevice device, int priority) {
17074ae04c73312403e89db0f8e9bd9601d403b4783fredc            HeadsetService service = getService();
17174ae04c73312403e89db0f8e9bd9601d403b4783fredc            if (service == null) return false;
17274ae04c73312403e89db0f8e9bd9601d403b4783fredc            return service.setPriority(device, priority);
1736c91bc0a163cc7600c40d7fb979777fd911d1ef1Matthew Xie        }
1746c91bc0a163cc7600c40d7fb979777fd911d1ef1Matthew Xie
1756c91bc0a163cc7600c40d7fb979777fd911d1ef1Matthew Xie        public int getPriority(BluetoothDevice device) {
17674ae04c73312403e89db0f8e9bd9601d403b4783fredc            HeadsetService service = getService();
17774ae04c73312403e89db0f8e9bd9601d403b4783fredc            if (service == null) return BluetoothProfile.PRIORITY_UNDEFINED;
17874ae04c73312403e89db0f8e9bd9601d403b4783fredc            return service.getPriority(device);
1796c91bc0a163cc7600c40d7fb979777fd911d1ef1Matthew Xie        }
1806c91bc0a163cc7600c40d7fb979777fd911d1ef1Matthew Xie
1816c91bc0a163cc7600c40d7fb979777fd911d1ef1Matthew Xie        public boolean startVoiceRecognition(BluetoothDevice device) {
18274ae04c73312403e89db0f8e9bd9601d403b4783fredc            HeadsetService service = getService();
18374ae04c73312403e89db0f8e9bd9601d403b4783fredc            if (service == null) return false;
18474ae04c73312403e89db0f8e9bd9601d403b4783fredc            return service.startVoiceRecognition(device);
1856c91bc0a163cc7600c40d7fb979777fd911d1ef1Matthew Xie        }
1866c91bc0a163cc7600c40d7fb979777fd911d1ef1Matthew Xie
1876c91bc0a163cc7600c40d7fb979777fd911d1ef1Matthew Xie        public boolean stopVoiceRecognition(BluetoothDevice device) {
18874ae04c73312403e89db0f8e9bd9601d403b4783fredc            HeadsetService service = getService();
18974ae04c73312403e89db0f8e9bd9601d403b4783fredc            if (service == null) return false;
19074ae04c73312403e89db0f8e9bd9601d403b4783fredc            return service.stopVoiceRecognition(device);
1916c91bc0a163cc7600c40d7fb979777fd911d1ef1Matthew Xie        }
1926c91bc0a163cc7600c40d7fb979777fd911d1ef1Matthew Xie
1936c91bc0a163cc7600c40d7fb979777fd911d1ef1Matthew Xie        public boolean isAudioOn() {
19474ae04c73312403e89db0f8e9bd9601d403b4783fredc            HeadsetService service = getService();
19574ae04c73312403e89db0f8e9bd9601d403b4783fredc            if (service == null) return false;
19674ae04c73312403e89db0f8e9bd9601d403b4783fredc            return service.isAudioOn();
1976c91bc0a163cc7600c40d7fb979777fd911d1ef1Matthew Xie        }
1986c91bc0a163cc7600c40d7fb979777fd911d1ef1Matthew Xie
1996c91bc0a163cc7600c40d7fb979777fd911d1ef1Matthew Xie        public boolean isAudioConnected(BluetoothDevice device) {
20074ae04c73312403e89db0f8e9bd9601d403b4783fredc            HeadsetService service = getService();
20174ae04c73312403e89db0f8e9bd9601d403b4783fredc            if (service == null) return false;
20274ae04c73312403e89db0f8e9bd9601d403b4783fredc            return service.isAudioConnected(device);
2036c91bc0a163cc7600c40d7fb979777fd911d1ef1Matthew Xie        }
2046c91bc0a163cc7600c40d7fb979777fd911d1ef1Matthew Xie
2056c91bc0a163cc7600c40d7fb979777fd911d1ef1Matthew Xie        public int getBatteryUsageHint(BluetoothDevice device) {
20674ae04c73312403e89db0f8e9bd9601d403b4783fredc            HeadsetService service = getService();
20774ae04c73312403e89db0f8e9bd9601d403b4783fredc            if (service == null) return 0;
20874ae04c73312403e89db0f8e9bd9601d403b4783fredc            return service.getBatteryUsageHint(device);
2096c91bc0a163cc7600c40d7fb979777fd911d1ef1Matthew Xie        }
2106c91bc0a163cc7600c40d7fb979777fd911d1ef1Matthew Xie
2116c91bc0a163cc7600c40d7fb979777fd911d1ef1Matthew Xie        public boolean acceptIncomingConnect(BluetoothDevice device) {
21274ae04c73312403e89db0f8e9bd9601d403b4783fredc            HeadsetService service = getService();
21374ae04c73312403e89db0f8e9bd9601d403b4783fredc            if (service == null) return false;
21474ae04c73312403e89db0f8e9bd9601d403b4783fredc            return service.acceptIncomingConnect(device);
2156c91bc0a163cc7600c40d7fb979777fd911d1ef1Matthew Xie        }
2166c91bc0a163cc7600c40d7fb979777fd911d1ef1Matthew Xie
2176c91bc0a163cc7600c40d7fb979777fd911d1ef1Matthew Xie        public boolean rejectIncomingConnect(BluetoothDevice device) {
21874ae04c73312403e89db0f8e9bd9601d403b4783fredc            HeadsetService service = getService();
21974ae04c73312403e89db0f8e9bd9601d403b4783fredc            if (service == null) return false;
22074ae04c73312403e89db0f8e9bd9601d403b4783fredc            return service.rejectIncomingConnect(device);
2216c91bc0a163cc7600c40d7fb979777fd911d1ef1Matthew Xie        }
2226c91bc0a163cc7600c40d7fb979777fd911d1ef1Matthew Xie
2236c91bc0a163cc7600c40d7fb979777fd911d1ef1Matthew Xie        public int getAudioState(BluetoothDevice device) {
22474ae04c73312403e89db0f8e9bd9601d403b4783fredc            HeadsetService service = getService();
22574ae04c73312403e89db0f8e9bd9601d403b4783fredc            if (service == null) return BluetoothHeadset.STATE_AUDIO_DISCONNECTED;
22674ae04c73312403e89db0f8e9bd9601d403b4783fredc            return service.getAudioState(device);
2276c91bc0a163cc7600c40d7fb979777fd911d1ef1Matthew Xie        }
2286c91bc0a163cc7600c40d7fb979777fd911d1ef1Matthew Xie
2296c91bc0a163cc7600c40d7fb979777fd911d1ef1Matthew Xie        public boolean connectAudio() {
23074ae04c73312403e89db0f8e9bd9601d403b4783fredc            HeadsetService service = getService();
23174ae04c73312403e89db0f8e9bd9601d403b4783fredc            if (service == null) return false;
23274ae04c73312403e89db0f8e9bd9601d403b4783fredc            return service.connectAudio();
2336c91bc0a163cc7600c40d7fb979777fd911d1ef1Matthew Xie        }
2346c91bc0a163cc7600c40d7fb979777fd911d1ef1Matthew Xie
2356c91bc0a163cc7600c40d7fb979777fd911d1ef1Matthew Xie        public boolean disconnectAudio() {
23674ae04c73312403e89db0f8e9bd9601d403b4783fredc            HeadsetService service = getService();
23774ae04c73312403e89db0f8e9bd9601d403b4783fredc            if (service == null) return false;
23874ae04c73312403e89db0f8e9bd9601d403b4783fredc            return service.disconnectAudio();
2396c91bc0a163cc7600c40d7fb979777fd911d1ef1Matthew Xie        }
2406c91bc0a163cc7600c40d7fb979777fd911d1ef1Matthew Xie
2416c91bc0a163cc7600c40d7fb979777fd911d1ef1Matthew Xie        public boolean startScoUsingVirtualVoiceCall(BluetoothDevice device) {
24274ae04c73312403e89db0f8e9bd9601d403b4783fredc            HeadsetService service = getService();
24374ae04c73312403e89db0f8e9bd9601d403b4783fredc            if (service == null) return false;
24474ae04c73312403e89db0f8e9bd9601d403b4783fredc            return service.startScoUsingVirtualVoiceCall(device);
2456c91bc0a163cc7600c40d7fb979777fd911d1ef1Matthew Xie        }
2466c91bc0a163cc7600c40d7fb979777fd911d1ef1Matthew Xie
2476c91bc0a163cc7600c40d7fb979777fd911d1ef1Matthew Xie        public boolean stopScoUsingVirtualVoiceCall(BluetoothDevice device) {
24874ae04c73312403e89db0f8e9bd9601d403b4783fredc            HeadsetService service = getService();
24974ae04c73312403e89db0f8e9bd9601d403b4783fredc            if (service == null) return false;
25074ae04c73312403e89db0f8e9bd9601d403b4783fredc            return service.stopScoUsingVirtualVoiceCall(device);
2516c91bc0a163cc7600c40d7fb979777fd911d1ef1Matthew Xie        }
2526c91bc0a163cc7600c40d7fb979777fd911d1ef1Matthew Xie
2536c91bc0a163cc7600c40d7fb979777fd911d1ef1Matthew Xie        public void phoneStateChanged(int numActive, int numHeld, int callState,
2546c91bc0a163cc7600c40d7fb979777fd911d1ef1Matthew Xie                                      String number, int type) {
25574ae04c73312403e89db0f8e9bd9601d403b4783fredc            HeadsetService service = getService();
25674ae04c73312403e89db0f8e9bd9601d403b4783fredc            if (service == null) return;
25774ae04c73312403e89db0f8e9bd9601d403b4783fredc            service.phoneStateChanged(numActive, numHeld, callState, number, type);
2586c91bc0a163cc7600c40d7fb979777fd911d1ef1Matthew Xie        }
2596c91bc0a163cc7600c40d7fb979777fd911d1ef1Matthew Xie
2606c91bc0a163cc7600c40d7fb979777fd911d1ef1Matthew Xie        public void roamChanged(boolean roam) {
26174ae04c73312403e89db0f8e9bd9601d403b4783fredc            HeadsetService service = getService();
26274ae04c73312403e89db0f8e9bd9601d403b4783fredc            if (service == null) return;
26374ae04c73312403e89db0f8e9bd9601d403b4783fredc            service.roamChanged(roam);
2646c91bc0a163cc7600c40d7fb979777fd911d1ef1Matthew Xie        }
2656c91bc0a163cc7600c40d7fb979777fd911d1ef1Matthew Xie
2666c91bc0a163cc7600c40d7fb979777fd911d1ef1Matthew Xie        public void clccResponse(int index, int direction, int status, int mode, boolean mpty,
2676c91bc0a163cc7600c40d7fb979777fd911d1ef1Matthew Xie                                 String number, int type) {
26874ae04c73312403e89db0f8e9bd9601d403b4783fredc            HeadsetService service = getService();
26974ae04c73312403e89db0f8e9bd9601d403b4783fredc            if (service == null) return;
27074ae04c73312403e89db0f8e9bd9601d403b4783fredc            service.clccResponse(index, direction, status, mode, mpty, number, type);
2716c91bc0a163cc7600c40d7fb979777fd911d1ef1Matthew Xie        }
2726c91bc0a163cc7600c40d7fb979777fd911d1ef1Matthew Xie    };
2736c91bc0a163cc7600c40d7fb979777fd911d1ef1Matthew Xie
27474ae04c73312403e89db0f8e9bd9601d403b4783fredc    //API methods
2754f5430babbc5a8f870e5a578a4ea3452f41dd97aGanesh Ganapathi Batta    public static synchronized HeadsetService getHeadsetService(){
2764f5430babbc5a8f870e5a578a4ea3452f41dd97aGanesh Ganapathi Batta        if (sHeadsetService != null && sHeadsetService.isAvailable()) {
2774f5430babbc5a8f870e5a578a4ea3452f41dd97aGanesh Ganapathi Batta            if (DBG) Log.d(TAG, "getHeadsetService(): returning " + sHeadsetService);
2784f5430babbc5a8f870e5a578a4ea3452f41dd97aGanesh Ganapathi Batta            return sHeadsetService;
2794f5430babbc5a8f870e5a578a4ea3452f41dd97aGanesh Ganapathi Batta        }
2804f5430babbc5a8f870e5a578a4ea3452f41dd97aGanesh Ganapathi Batta        if (DBG)  {
2814f5430babbc5a8f870e5a578a4ea3452f41dd97aGanesh Ganapathi Batta            if (sHeadsetService == null) {
2824f5430babbc5a8f870e5a578a4ea3452f41dd97aGanesh Ganapathi Batta                Log.d(TAG, "getHeadsetService(): service is NULL");
2834f5430babbc5a8f870e5a578a4ea3452f41dd97aGanesh Ganapathi Batta            } else if (!(sHeadsetService.isAvailable())) {
2844f5430babbc5a8f870e5a578a4ea3452f41dd97aGanesh Ganapathi Batta                Log.d(TAG,"getHeadsetService(): service is not available");
2854f5430babbc5a8f870e5a578a4ea3452f41dd97aGanesh Ganapathi Batta            }
2864f5430babbc5a8f870e5a578a4ea3452f41dd97aGanesh Ganapathi Batta        }
2874f5430babbc5a8f870e5a578a4ea3452f41dd97aGanesh Ganapathi Batta        return null;
2884f5430babbc5a8f870e5a578a4ea3452f41dd97aGanesh Ganapathi Batta    }
2894f5430babbc5a8f870e5a578a4ea3452f41dd97aGanesh Ganapathi Batta
2904f5430babbc5a8f870e5a578a4ea3452f41dd97aGanesh Ganapathi Batta    private static synchronized void setHeadsetService(HeadsetService instance) {
2914f5430babbc5a8f870e5a578a4ea3452f41dd97aGanesh Ganapathi Batta        if (instance != null && instance.isAvailable()) {
2924f5430babbc5a8f870e5a578a4ea3452f41dd97aGanesh Ganapathi Batta            if (DBG) Log.d(TAG, "setHeadsetService(): set to: " + sHeadsetService);
2934f5430babbc5a8f870e5a578a4ea3452f41dd97aGanesh Ganapathi Batta            sHeadsetService = instance;
2944f5430babbc5a8f870e5a578a4ea3452f41dd97aGanesh Ganapathi Batta        } else {
2954f5430babbc5a8f870e5a578a4ea3452f41dd97aGanesh Ganapathi Batta            if (DBG)  {
2964f5430babbc5a8f870e5a578a4ea3452f41dd97aGanesh Ganapathi Batta                if (sHeadsetService == null) {
2974f5430babbc5a8f870e5a578a4ea3452f41dd97aGanesh Ganapathi Batta                    Log.d(TAG, "setHeadsetService(): service not available");
2984f5430babbc5a8f870e5a578a4ea3452f41dd97aGanesh Ganapathi Batta                } else if (!sHeadsetService.isAvailable()) {
2994f5430babbc5a8f870e5a578a4ea3452f41dd97aGanesh Ganapathi Batta                    Log.d(TAG,"setHeadsetService(): service is cleaning up");
3004f5430babbc5a8f870e5a578a4ea3452f41dd97aGanesh Ganapathi Batta                }
3014f5430babbc5a8f870e5a578a4ea3452f41dd97aGanesh Ganapathi Batta            }
3024f5430babbc5a8f870e5a578a4ea3452f41dd97aGanesh Ganapathi Batta        }
3034f5430babbc5a8f870e5a578a4ea3452f41dd97aGanesh Ganapathi Batta    }
3044f5430babbc5a8f870e5a578a4ea3452f41dd97aGanesh Ganapathi Batta
3054f5430babbc5a8f870e5a578a4ea3452f41dd97aGanesh Ganapathi Batta    private static synchronized void clearHeadsetService() {
3064f5430babbc5a8f870e5a578a4ea3452f41dd97aGanesh Ganapathi Batta        sHeadsetService = null;
3074f5430babbc5a8f870e5a578a4ea3452f41dd97aGanesh Ganapathi Batta    }
3084f5430babbc5a8f870e5a578a4ea3452f41dd97aGanesh Ganapathi Batta
3094f5430babbc5a8f870e5a578a4ea3452f41dd97aGanesh Ganapathi Batta    public boolean connect(BluetoothDevice device) {
31074ae04c73312403e89db0f8e9bd9601d403b4783fredc        enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
31174ae04c73312403e89db0f8e9bd9601d403b4783fredc                                       "Need BLUETOOTH ADMIN permission");
31274ae04c73312403e89db0f8e9bd9601d403b4783fredc
31374ae04c73312403e89db0f8e9bd9601d403b4783fredc        if (getPriority(device) == BluetoothProfile.PRIORITY_OFF) {
31474ae04c73312403e89db0f8e9bd9601d403b4783fredc            return false;
31574ae04c73312403e89db0f8e9bd9601d403b4783fredc        }
31674ae04c73312403e89db0f8e9bd9601d403b4783fredc
31774ae04c73312403e89db0f8e9bd9601d403b4783fredc        int connectionState = mStateMachine.getConnectionState(device);
31874ae04c73312403e89db0f8e9bd9601d403b4783fredc        if (connectionState == BluetoothProfile.STATE_CONNECTED ||
31974ae04c73312403e89db0f8e9bd9601d403b4783fredc            connectionState == BluetoothProfile.STATE_CONNECTING) {
32074ae04c73312403e89db0f8e9bd9601d403b4783fredc            return false;
32174ae04c73312403e89db0f8e9bd9601d403b4783fredc        }
32274ae04c73312403e89db0f8e9bd9601d403b4783fredc
32374ae04c73312403e89db0f8e9bd9601d403b4783fredc        mStateMachine.sendMessage(HeadsetStateMachine.CONNECT, device);
32474ae04c73312403e89db0f8e9bd9601d403b4783fredc        return true;
32574ae04c73312403e89db0f8e9bd9601d403b4783fredc    }
32674ae04c73312403e89db0f8e9bd9601d403b4783fredc
32774ae04c73312403e89db0f8e9bd9601d403b4783fredc    boolean disconnect(BluetoothDevice device) {
32874ae04c73312403e89db0f8e9bd9601d403b4783fredc        enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
32974ae04c73312403e89db0f8e9bd9601d403b4783fredc                                       "Need BLUETOOTH ADMIN permission");
33074ae04c73312403e89db0f8e9bd9601d403b4783fredc        int connectionState = mStateMachine.getConnectionState(device);
33174ae04c73312403e89db0f8e9bd9601d403b4783fredc        if (connectionState != BluetoothProfile.STATE_CONNECTED &&
33274ae04c73312403e89db0f8e9bd9601d403b4783fredc            connectionState != BluetoothProfile.STATE_CONNECTING) {
33374ae04c73312403e89db0f8e9bd9601d403b4783fredc            return false;
33474ae04c73312403e89db0f8e9bd9601d403b4783fredc        }
33574ae04c73312403e89db0f8e9bd9601d403b4783fredc
33674ae04c73312403e89db0f8e9bd9601d403b4783fredc        mStateMachine.sendMessage(HeadsetStateMachine.DISCONNECT, device);
33774ae04c73312403e89db0f8e9bd9601d403b4783fredc        return true;
33874ae04c73312403e89db0f8e9bd9601d403b4783fredc    }
33974ae04c73312403e89db0f8e9bd9601d403b4783fredc
3404f5430babbc5a8f870e5a578a4ea3452f41dd97aGanesh Ganapathi Batta    public List<BluetoothDevice> getConnectedDevices() {
34174ae04c73312403e89db0f8e9bd9601d403b4783fredc        enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
34274ae04c73312403e89db0f8e9bd9601d403b4783fredc        return mStateMachine.getConnectedDevices();
34374ae04c73312403e89db0f8e9bd9601d403b4783fredc    }
34474ae04c73312403e89db0f8e9bd9601d403b4783fredc
34574ae04c73312403e89db0f8e9bd9601d403b4783fredc    private List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
34674ae04c73312403e89db0f8e9bd9601d403b4783fredc        enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
34774ae04c73312403e89db0f8e9bd9601d403b4783fredc        return mStateMachine.getDevicesMatchingConnectionStates(states);
34874ae04c73312403e89db0f8e9bd9601d403b4783fredc    }
34974ae04c73312403e89db0f8e9bd9601d403b4783fredc
35074ae04c73312403e89db0f8e9bd9601d403b4783fredc    int getConnectionState(BluetoothDevice device) {
35174ae04c73312403e89db0f8e9bd9601d403b4783fredc        enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
35274ae04c73312403e89db0f8e9bd9601d403b4783fredc        return mStateMachine.getConnectionState(device);
35374ae04c73312403e89db0f8e9bd9601d403b4783fredc    }
35474ae04c73312403e89db0f8e9bd9601d403b4783fredc
3554f5430babbc5a8f870e5a578a4ea3452f41dd97aGanesh Ganapathi Batta    public boolean setPriority(BluetoothDevice device, int priority) {
35674ae04c73312403e89db0f8e9bd9601d403b4783fredc        enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
35774ae04c73312403e89db0f8e9bd9601d403b4783fredc                                       "Need BLUETOOTH_ADMIN permission");
358c7ead6d5827f3832b9210e92fab7597142059852Jeff Sharkey        Settings.Global.putInt(getContentResolver(),
359c7ead6d5827f3832b9210e92fab7597142059852Jeff Sharkey            Settings.Global.getBluetoothHeadsetPriorityKey(device.getAddress()),
36074ae04c73312403e89db0f8e9bd9601d403b4783fredc            priority);
36174ae04c73312403e89db0f8e9bd9601d403b4783fredc        if (DBG) Log.d(TAG, "Saved priority " + device + " = " + priority);
36274ae04c73312403e89db0f8e9bd9601d403b4783fredc        return true;
36374ae04c73312403e89db0f8e9bd9601d403b4783fredc    }
36474ae04c73312403e89db0f8e9bd9601d403b4783fredc
3654f5430babbc5a8f870e5a578a4ea3452f41dd97aGanesh Ganapathi Batta    public int getPriority(BluetoothDevice device) {
36674ae04c73312403e89db0f8e9bd9601d403b4783fredc        enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
36774ae04c73312403e89db0f8e9bd9601d403b4783fredc                                       "Need BLUETOOTH_ADMIN permission");
368c7ead6d5827f3832b9210e92fab7597142059852Jeff Sharkey        int priority = Settings.Global.getInt(getContentResolver(),
369c7ead6d5827f3832b9210e92fab7597142059852Jeff Sharkey            Settings.Global.getBluetoothHeadsetPriorityKey(device.getAddress()),
37074ae04c73312403e89db0f8e9bd9601d403b4783fredc            BluetoothProfile.PRIORITY_UNDEFINED);
37174ae04c73312403e89db0f8e9bd9601d403b4783fredc        return priority;
37274ae04c73312403e89db0f8e9bd9601d403b4783fredc    }
37374ae04c73312403e89db0f8e9bd9601d403b4783fredc
37474ae04c73312403e89db0f8e9bd9601d403b4783fredc    boolean startVoiceRecognition(BluetoothDevice device) {
37574ae04c73312403e89db0f8e9bd9601d403b4783fredc        enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
37674ae04c73312403e89db0f8e9bd9601d403b4783fredc        int connectionState = mStateMachine.getConnectionState(device);
37774ae04c73312403e89db0f8e9bd9601d403b4783fredc        if (connectionState != BluetoothProfile.STATE_CONNECTED &&
37874ae04c73312403e89db0f8e9bd9601d403b4783fredc            connectionState != BluetoothProfile.STATE_CONNECTING) {
37974ae04c73312403e89db0f8e9bd9601d403b4783fredc            return false;
38074ae04c73312403e89db0f8e9bd9601d403b4783fredc        }
38174ae04c73312403e89db0f8e9bd9601d403b4783fredc        mStateMachine.sendMessage(HeadsetStateMachine.VOICE_RECOGNITION_START);
38274ae04c73312403e89db0f8e9bd9601d403b4783fredc        return true;
38374ae04c73312403e89db0f8e9bd9601d403b4783fredc    }
38474ae04c73312403e89db0f8e9bd9601d403b4783fredc
38574ae04c73312403e89db0f8e9bd9601d403b4783fredc    boolean stopVoiceRecognition(BluetoothDevice device) {
38674ae04c73312403e89db0f8e9bd9601d403b4783fredc        enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
38774ae04c73312403e89db0f8e9bd9601d403b4783fredc        // It seem that we really need to check the AudioOn state.
38874ae04c73312403e89db0f8e9bd9601d403b4783fredc        // But since we allow startVoiceRecognition in STATE_CONNECTED and
38974ae04c73312403e89db0f8e9bd9601d403b4783fredc        // STATE_CONNECTING state, we do these 2 in this method
39074ae04c73312403e89db0f8e9bd9601d403b4783fredc        int connectionState = mStateMachine.getConnectionState(device);
39174ae04c73312403e89db0f8e9bd9601d403b4783fredc        if (connectionState != BluetoothProfile.STATE_CONNECTED &&
39274ae04c73312403e89db0f8e9bd9601d403b4783fredc            connectionState != BluetoothProfile.STATE_CONNECTING) {
39374ae04c73312403e89db0f8e9bd9601d403b4783fredc            return false;
39474ae04c73312403e89db0f8e9bd9601d403b4783fredc        }
39574ae04c73312403e89db0f8e9bd9601d403b4783fredc        mStateMachine.sendMessage(HeadsetStateMachine.VOICE_RECOGNITION_STOP);
39674ae04c73312403e89db0f8e9bd9601d403b4783fredc        // TODO is this return correct when the voice recognition is not on?
39774ae04c73312403e89db0f8e9bd9601d403b4783fredc        return true;
39874ae04c73312403e89db0f8e9bd9601d403b4783fredc    }
39974ae04c73312403e89db0f8e9bd9601d403b4783fredc
40074ae04c73312403e89db0f8e9bd9601d403b4783fredc    boolean isAudioOn() {
40174ae04c73312403e89db0f8e9bd9601d403b4783fredc        enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
40274ae04c73312403e89db0f8e9bd9601d403b4783fredc        return mStateMachine.isAudioOn();
40374ae04c73312403e89db0f8e9bd9601d403b4783fredc    }
40474ae04c73312403e89db0f8e9bd9601d403b4783fredc
40574ae04c73312403e89db0f8e9bd9601d403b4783fredc    boolean isAudioConnected(BluetoothDevice device) {
40674ae04c73312403e89db0f8e9bd9601d403b4783fredc        enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
40774ae04c73312403e89db0f8e9bd9601d403b4783fredc        return mStateMachine.isAudioConnected(device);
40874ae04c73312403e89db0f8e9bd9601d403b4783fredc    }
40974ae04c73312403e89db0f8e9bd9601d403b4783fredc
41074ae04c73312403e89db0f8e9bd9601d403b4783fredc    int getBatteryUsageHint(BluetoothDevice device) {
41174ae04c73312403e89db0f8e9bd9601d403b4783fredc        // TODO(BT) ask for BT stack support?
41274ae04c73312403e89db0f8e9bd9601d403b4783fredc        return 0;
41374ae04c73312403e89db0f8e9bd9601d403b4783fredc    }
41474ae04c73312403e89db0f8e9bd9601d403b4783fredc
41574ae04c73312403e89db0f8e9bd9601d403b4783fredc    boolean acceptIncomingConnect(BluetoothDevice device) {
41674ae04c73312403e89db0f8e9bd9601d403b4783fredc        // TODO(BT) remove it if stack does access control
41774ae04c73312403e89db0f8e9bd9601d403b4783fredc        return false;
41874ae04c73312403e89db0f8e9bd9601d403b4783fredc    }
41974ae04c73312403e89db0f8e9bd9601d403b4783fredc
42074ae04c73312403e89db0f8e9bd9601d403b4783fredc    boolean rejectIncomingConnect(BluetoothDevice device) {
42174ae04c73312403e89db0f8e9bd9601d403b4783fredc        // TODO(BT) remove it if stack does access control
42274ae04c73312403e89db0f8e9bd9601d403b4783fredc        return false;
42374ae04c73312403e89db0f8e9bd9601d403b4783fredc    }
42474ae04c73312403e89db0f8e9bd9601d403b4783fredc
42574ae04c73312403e89db0f8e9bd9601d403b4783fredc    int getAudioState(BluetoothDevice device) {
42674ae04c73312403e89db0f8e9bd9601d403b4783fredc        return mStateMachine.getAudioState(device);
42774ae04c73312403e89db0f8e9bd9601d403b4783fredc    }
42874ae04c73312403e89db0f8e9bd9601d403b4783fredc
42974ae04c73312403e89db0f8e9bd9601d403b4783fredc    boolean connectAudio() {
43074ae04c73312403e89db0f8e9bd9601d403b4783fredc        // TODO(BT) BLUETOOTH or BLUETOOTH_ADMIN permission
43174ae04c73312403e89db0f8e9bd9601d403b4783fredc        enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
43274ae04c73312403e89db0f8e9bd9601d403b4783fredc        if (!mStateMachine.isConnected()) {
43374ae04c73312403e89db0f8e9bd9601d403b4783fredc            return false;
43474ae04c73312403e89db0f8e9bd9601d403b4783fredc        }
43574ae04c73312403e89db0f8e9bd9601d403b4783fredc        if (mStateMachine.isAudioOn()) {
43674ae04c73312403e89db0f8e9bd9601d403b4783fredc            return false;
43774ae04c73312403e89db0f8e9bd9601d403b4783fredc        }
43874ae04c73312403e89db0f8e9bd9601d403b4783fredc        mStateMachine.sendMessage(HeadsetStateMachine.CONNECT_AUDIO);
43974ae04c73312403e89db0f8e9bd9601d403b4783fredc        return true;
44074ae04c73312403e89db0f8e9bd9601d403b4783fredc    }
44174ae04c73312403e89db0f8e9bd9601d403b4783fredc
44274ae04c73312403e89db0f8e9bd9601d403b4783fredc    boolean disconnectAudio() {
44374ae04c73312403e89db0f8e9bd9601d403b4783fredc        // TODO(BT) BLUETOOTH or BLUETOOTH_ADMIN permission
44474ae04c73312403e89db0f8e9bd9601d403b4783fredc        enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
44574ae04c73312403e89db0f8e9bd9601d403b4783fredc        if (!mStateMachine.isAudioOn()) {
44674ae04c73312403e89db0f8e9bd9601d403b4783fredc            return false;
44774ae04c73312403e89db0f8e9bd9601d403b4783fredc        }
44874ae04c73312403e89db0f8e9bd9601d403b4783fredc        mStateMachine.sendMessage(HeadsetStateMachine.DISCONNECT_AUDIO);
44974ae04c73312403e89db0f8e9bd9601d403b4783fredc        return true;
45074ae04c73312403e89db0f8e9bd9601d403b4783fredc    }
45174ae04c73312403e89db0f8e9bd9601d403b4783fredc
45274ae04c73312403e89db0f8e9bd9601d403b4783fredc    boolean startScoUsingVirtualVoiceCall(BluetoothDevice device) {
4536b19b0e94877ae8f0803133f8cfb4885acff2763Syed Ibrahim M        int connectionState = mStateMachine.getConnectionState(device);
4546b19b0e94877ae8f0803133f8cfb4885acff2763Syed Ibrahim M        if (connectionState != BluetoothProfile.STATE_CONNECTED &&
4556b19b0e94877ae8f0803133f8cfb4885acff2763Syed Ibrahim M            connectionState != BluetoothProfile.STATE_CONNECTING) {
4566b19b0e94877ae8f0803133f8cfb4885acff2763Syed Ibrahim M            return false;
4576b19b0e94877ae8f0803133f8cfb4885acff2763Syed Ibrahim M        }
4586b19b0e94877ae8f0803133f8cfb4885acff2763Syed Ibrahim M        mStateMachine.sendMessage(HeadsetStateMachine.VIRTUAL_CALL_START, device);
45974ae04c73312403e89db0f8e9bd9601d403b4783fredc        return true;
46074ae04c73312403e89db0f8e9bd9601d403b4783fredc    }
46174ae04c73312403e89db0f8e9bd9601d403b4783fredc
46274ae04c73312403e89db0f8e9bd9601d403b4783fredc    boolean stopScoUsingVirtualVoiceCall(BluetoothDevice device) {
4636b19b0e94877ae8f0803133f8cfb4885acff2763Syed Ibrahim M        int connectionState = mStateMachine.getConnectionState(device);
4646b19b0e94877ae8f0803133f8cfb4885acff2763Syed Ibrahim M        if (connectionState != BluetoothProfile.STATE_CONNECTED &&
4656b19b0e94877ae8f0803133f8cfb4885acff2763Syed Ibrahim M            connectionState != BluetoothProfile.STATE_CONNECTING) {
4666b19b0e94877ae8f0803133f8cfb4885acff2763Syed Ibrahim M            return false;
4676b19b0e94877ae8f0803133f8cfb4885acff2763Syed Ibrahim M        }
4686b19b0e94877ae8f0803133f8cfb4885acff2763Syed Ibrahim M        mStateMachine.sendMessage(HeadsetStateMachine.VIRTUAL_CALL_STOP, device);
46974ae04c73312403e89db0f8e9bd9601d403b4783fredc        return true;
47074ae04c73312403e89db0f8e9bd9601d403b4783fredc    }
47174ae04c73312403e89db0f8e9bd9601d403b4783fredc
47274ae04c73312403e89db0f8e9bd9601d403b4783fredc    private void phoneStateChanged(int numActive, int numHeld, int callState,
47374ae04c73312403e89db0f8e9bd9601d403b4783fredc                                  String number, int type) {
4740e46362107a23fcfe829b270f3ff987d911cd102Matthew Xie        enforceCallingOrSelfPermission(MODIFY_PHONE_STATE, null);
4756b19b0e94877ae8f0803133f8cfb4885acff2763Syed Ibrahim M        Message msg = mStateMachine.obtainMessage(HeadsetStateMachine.CALL_STATE_CHANGED);
4766b19b0e94877ae8f0803133f8cfb4885acff2763Syed Ibrahim M        msg.obj = new HeadsetCallState(numActive, numHeld, callState, number, type);
4776b19b0e94877ae8f0803133f8cfb4885acff2763Syed Ibrahim M        msg.arg1 = 0; // false
4786b19b0e94877ae8f0803133f8cfb4885acff2763Syed Ibrahim M        mStateMachine.sendMessage(msg);
47974ae04c73312403e89db0f8e9bd9601d403b4783fredc    }
48074ae04c73312403e89db0f8e9bd9601d403b4783fredc
48174ae04c73312403e89db0f8e9bd9601d403b4783fredc    private void roamChanged(boolean roam) {
4820e46362107a23fcfe829b270f3ff987d911cd102Matthew Xie        enforceCallingOrSelfPermission(MODIFY_PHONE_STATE, null);
48374ae04c73312403e89db0f8e9bd9601d403b4783fredc        mStateMachine.sendMessage(HeadsetStateMachine.ROAM_CHANGED, roam);
48474ae04c73312403e89db0f8e9bd9601d403b4783fredc    }
48574ae04c73312403e89db0f8e9bd9601d403b4783fredc
48674ae04c73312403e89db0f8e9bd9601d403b4783fredc    private void clccResponse(int index, int direction, int status, int mode, boolean mpty,
48774ae04c73312403e89db0f8e9bd9601d403b4783fredc                             String number, int type) {
4880e46362107a23fcfe829b270f3ff987d911cd102Matthew Xie        enforceCallingOrSelfPermission(MODIFY_PHONE_STATE, null);
48974ae04c73312403e89db0f8e9bd9601d403b4783fredc        mStateMachine.sendMessage(HeadsetStateMachine.SEND_CCLC_RESPONSE,
49074ae04c73312403e89db0f8e9bd9601d403b4783fredc            new HeadsetClccResponse(index, direction, status, mode, mpty, number, type));
49174ae04c73312403e89db0f8e9bd9601d403b4783fredc    }
49274ae04c73312403e89db0f8e9bd9601d403b4783fredc
4936c91bc0a163cc7600c40d7fb979777fd911d1ef1Matthew Xie}
494