HeadsetClientService.java revision 84f977c60b8d7feea9c9b20d60a7eb8e3e4869a9
1aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta/*
2aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta * Copyright (c) 2014 The Android Open Source Project
3aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta *
4aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta * Licensed under the Apache License, Version 2.0 (the "License");
5aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta * you may not use this file except in compliance with the License.
6aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta * You may obtain a copy of the License at
7aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta *
8aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta *      http://www.apache.org/licenses/LICENSE-2.0
9aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta *
10aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta * Unless required by applicable law or agreed to in writing, software
11aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta * distributed under the License is distributed on an "AS IS" BASIS,
12aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta * See the License for the specific language governing permissions and
14aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta * limitations under the License.
15aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta */
16aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta
17aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Guptapackage com.android.bluetooth.hfpclient;
18aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta
19aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Guptaimport android.bluetooth.BluetoothDevice;
20aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Guptaimport android.bluetooth.BluetoothProfile;
218d536f3db19e8ea7426e98e470dc15d10ecbae87Mike Lockwoodimport android.bluetooth.BluetoothHeadsetClient;
228d536f3db19e8ea7426e98e470dc15d10ecbae87Mike Lockwoodimport android.bluetooth.BluetoothHeadsetClientCall;
238d536f3db19e8ea7426e98e470dc15d10ecbae87Mike Lockwoodimport android.bluetooth.IBluetoothHeadsetClient;
24aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Guptaimport android.content.BroadcastReceiver;
25aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Guptaimport android.content.Context;
26aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Guptaimport android.content.Intent;
27aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Guptaimport android.content.IntentFilter;
28aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Guptaimport android.media.AudioManager;
29aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Guptaimport android.os.Bundle;
3084f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwalimport android.os.HandlerThread;
3184f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwalimport android.os.Looper;
32aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Guptaimport android.os.Message;
33aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Guptaimport android.provider.Settings;
34aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Guptaimport android.util.Log;
35eb7b90f5b93db1230a5b64caa3d8d05a642e33a6Marie Janssen
36aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Guptaimport com.android.bluetooth.btservice.ProfileService;
37a8fc504fed7dcb5dc074980b9d4cfa7a56445424Sanket Agarwalimport com.android.bluetooth.hfpclient.connserv.HfpClientConnectionService;
38aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Guptaimport com.android.bluetooth.Utils;
39eb7b90f5b93db1230a5b64caa3d8d05a642e33a6Marie Janssen
40aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Guptaimport java.util.ArrayList;
4184f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwalimport java.util.HashMap;
4284f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwalimport java.util.Iterator;
43aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Guptaimport java.util.List;
4484f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwalimport java.util.Map;
45ca6e7c65b87c52f5e81b273d7ad979a81b52f991Sanket Agarwalimport java.util.UUID;
46a8fc504fed7dcb5dc074980b9d4cfa7a56445424Sanket Agarwal
47aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta/**
488d536f3db19e8ea7426e98e470dc15d10ecbae87Mike Lockwood * Provides Bluetooth Headset Client (HF Role) profile, as a service in the
49aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta * Bluetooth application.
50aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta *
51aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta * @hide
52aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta */
538d536f3db19e8ea7426e98e470dc15d10ecbae87Mike Lockwoodpublic class HeadsetClientService extends ProfileService {
54aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta    private static final boolean DBG = false;
558d536f3db19e8ea7426e98e470dc15d10ecbae87Mike Lockwood    private static final String TAG = "HeadsetClientService";
56aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta
5784f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal    private HashMap<BluetoothDevice, HeadsetClientStateMachine> mStateMachineMap =
5884f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal        new HashMap<>();
598d536f3db19e8ea7426e98e470dc15d10ecbae87Mike Lockwood    private static HeadsetClientService sHeadsetClientService;
6084f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal    private NativeInterface mNativeInterface = null;
6184f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal    private HandlerThread mSmThread = null;
6284f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal    private HeadsetClientStateMachineFactory mSmFactory = null;
6384f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal    // Maxinum number of devices we can try connecting to in one session
6484f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal    private static final int MAX_STATE_MACHINES_POSSIBLE = 100;
65aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta
66a8fc504fed7dcb5dc074980b9d4cfa7a56445424Sanket Agarwal    public static String HFP_CLIENT_STOP_TAG = "hfp_client_stop_tag";
67a8fc504fed7dcb5dc074980b9d4cfa7a56445424Sanket Agarwal
6884f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal    static {
6984f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal        NativeInterface.initializeNative();
7084f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal    }
7184f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal
72aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta    @Override
73aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta    protected String getName() {
74aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta        return TAG;
75aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta    }
76aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta
77aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta    @Override
78aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta    public IProfileServiceBinder initBinder() {
798d536f3db19e8ea7426e98e470dc15d10ecbae87Mike Lockwood        return new BluetoothHeadsetClientBinder(this);
80aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta    }
81aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta
82aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta    @Override
8384f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal    protected synchronized boolean start() {
8484f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal        mSmFactory = new HeadsetClientStateMachineFactory();
8584f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal        mStateMachineMap.clear();
8684f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal
87aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta        IntentFilter filter = new IntentFilter(AudioManager.VOLUME_CHANGED_ACTION);
88aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta        try {
89aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta            registerReceiver(mBroadcastReceiver, filter);
90aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta        } catch (Exception e) {
91aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta            Log.w(TAG, "Unable to register broadcat receiver", e);
92aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta        }
938d536f3db19e8ea7426e98e470dc15d10ecbae87Mike Lockwood        setHeadsetClientService(this);
9484f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal        mNativeInterface = new NativeInterface();
95a8fc504fed7dcb5dc074980b9d4cfa7a56445424Sanket Agarwal
96a8fc504fed7dcb5dc074980b9d4cfa7a56445424Sanket Agarwal        // Start the HfpClientConnectionService to create connection with telecom when HFP
97a8fc504fed7dcb5dc074980b9d4cfa7a56445424Sanket Agarwal        // connection is available.
98a8fc504fed7dcb5dc074980b9d4cfa7a56445424Sanket Agarwal        Intent startIntent = new Intent(this, HfpClientConnectionService.class);
99a8fc504fed7dcb5dc074980b9d4cfa7a56445424Sanket Agarwal        startService(startIntent);
100a8fc504fed7dcb5dc074980b9d4cfa7a56445424Sanket Agarwal
10184f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal        // Create the thread on which all State Machines will run
10284f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal        mSmThread = new HandlerThread("HeadsetClient.SM");
10384f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal        mSmThread.start();
10484f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal        NativeInterface.initializeNative();
10584f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal
106aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta        return true;
107aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta    }
108aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta
109aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta    @Override
11084f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal    protected synchronized boolean stop() {
111aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta        try {
112aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta            unregisterReceiver(mBroadcastReceiver);
113aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta        } catch (Exception e) {
114aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta            Log.w(TAG, "Unable to unregister broadcast receiver", e);
115aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta        }
116562547733c2dd517f3bdd30f29bfe43732fe9a13Chenjie Luo
11784f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal        for (Iterator<Map.Entry<BluetoothDevice, HeadsetClientStateMachine>> it =
11884f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal                mStateMachineMap.entrySet().iterator(); it.hasNext(); ) {
11984f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal            HeadsetClientStateMachine sm =
12084f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal                mStateMachineMap.get((BluetoothDevice) it.next().getKey());
12184f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal            sm.doQuit();
12284f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal            it.remove();
123bebc4dd9edb40710d9588b4a2010eef227756a8eLianchao Song        }
124a8fc504fed7dcb5dc074980b9d4cfa7a56445424Sanket Agarwal
125a8fc504fed7dcb5dc074980b9d4cfa7a56445424Sanket Agarwal        // Stop the HfpClientConnectionService.
126a8fc504fed7dcb5dc074980b9d4cfa7a56445424Sanket Agarwal        Intent stopIntent = new Intent(this, HfpClientConnectionService.class);
127a8fc504fed7dcb5dc074980b9d4cfa7a56445424Sanket Agarwal        stopIntent.putExtra(HFP_CLIENT_STOP_TAG, true);
128a8fc504fed7dcb5dc074980b9d4cfa7a56445424Sanket Agarwal        startService(stopIntent);
12984f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal        mNativeInterface = null;
13084f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal
13184f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal        // Stop the handler thread
13284f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal        mSmThread.quit();
13384f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal        mSmThread = null;
13484f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal
13584f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal        NativeInterface.cleanupNative();
13684f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal
137aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta        return true;
138aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta    }
139aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta
140aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta    @Override
141aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta    protected boolean cleanup() {
14284f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal        HeadsetClientStateMachine.cleanup();
1438d536f3db19e8ea7426e98e470dc15d10ecbae87Mike Lockwood        clearHeadsetClientService();
144aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta        return true;
145aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta    }
146aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta
147aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta    private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
148aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta        @Override
149aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta        public void onReceive(Context context, Intent intent) {
150aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta            String action = intent.getAction();
151aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta
1523076d2acccf5c3db0db19b622f062e259be39f3aSanket Agarwal            // We handle the volume changes for Voice calls here since HFP audio volume control does
1533076d2acccf5c3db0db19b622f062e259be39f3aSanket Agarwal            // not go through audio manager (audio mixer). We check if the voice call volume has
1543076d2acccf5c3db0db19b622f062e259be39f3aSanket Agarwal            // changed and subsequently change the SCO volume see
1553076d2acccf5c3db0db19b622f062e259be39f3aSanket Agarwal            // ({@link HeadsetClientStateMachine#SET_SPEAKER_VOLUME} in
1563076d2acccf5c3db0db19b622f062e259be39f3aSanket Agarwal            // {@link HeadsetClientStateMachine} for details.
157aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta            if (action.equals(AudioManager.VOLUME_CHANGED_ACTION)) {
1583076d2acccf5c3db0db19b622f062e259be39f3aSanket Agarwal                Log.d(TAG, "Volume changed for stream: " +
1593076d2acccf5c3db0db19b622f062e259be39f3aSanket Agarwal                    intent.getExtra(AudioManager.EXTRA_VOLUME_STREAM_TYPE));
160aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta                int streamType = intent.getIntExtra(AudioManager.EXTRA_VOLUME_STREAM_TYPE, -1);
1613076d2acccf5c3db0db19b622f062e259be39f3aSanket Agarwal                if (streamType == AudioManager.STREAM_VOICE_CALL) {
162aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta                    int streamValue = intent
163aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta                            .getIntExtra(AudioManager.EXTRA_VOLUME_STREAM_VALUE, -1);
164aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta                    int streamPrevValue = intent.getIntExtra(
165aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta                            AudioManager.EXTRA_PREV_VOLUME_STREAM_VALUE, -1);
166aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta
167aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta                    if (streamValue != -1 && streamValue != streamPrevValue) {
16884f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal                        // TODO: Fix this
16984f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal                        // sm.sendMessage(sm.obtainMessage(
17084f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal                        //         HeadsetClientStateMachine.SET_SPEAKER_VOLUME, streamValue, 0));
171aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta                    }
172aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta                }
173aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta            }
174aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta        }
175aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta    };
176aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta
177aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta    /**
178aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta     * Handlers for incoming service calls
179aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta     */
1808d536f3db19e8ea7426e98e470dc15d10ecbae87Mike Lockwood    private static class BluetoothHeadsetClientBinder extends IBluetoothHeadsetClient.Stub
181aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta            implements IProfileServiceBinder {
1828d536f3db19e8ea7426e98e470dc15d10ecbae87Mike Lockwood        private HeadsetClientService mService;
183aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta
1848d536f3db19e8ea7426e98e470dc15d10ecbae87Mike Lockwood        public BluetoothHeadsetClientBinder(HeadsetClientService svc) {
185aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta            mService = svc;
186aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta        }
187aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta
188aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta        @Override
189aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta        public boolean cleanup() {
190aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta            mService = null;
191aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta            return true;
192aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta        }
193aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta
1948d536f3db19e8ea7426e98e470dc15d10ecbae87Mike Lockwood        private HeadsetClientService getService() {
195aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta            if (!Utils.checkCaller()) {
1968d536f3db19e8ea7426e98e470dc15d10ecbae87Mike Lockwood                Log.w(TAG, "HeadsetClient call not allowed for non-active user");
197aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta                return null;
198aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta            }
199aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta
200aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta            if (mService != null && mService.isAvailable()) {
201aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta                return mService;
202aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta            }
20333acd2208455171249084dd86ae0ed95db1e4c49Wei Liu
20433acd2208455171249084dd86ae0ed95db1e4c49Wei Liu            Log.e(TAG, "HeadsetClientService is not available.");
205aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta            return null;
206aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta        }
207aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta
208aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta        @Override
209aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta        public boolean connect(BluetoothDevice device) {
2108d536f3db19e8ea7426e98e470dc15d10ecbae87Mike Lockwood            HeadsetClientService service = getService();
211aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta            if (service == null) {
212aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta                return false;
213aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta            }
214aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta            return service.connect(device);
215aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta        }
216aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta
217aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta        @Override
218aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta        public boolean disconnect(BluetoothDevice device) {
2198d536f3db19e8ea7426e98e470dc15d10ecbae87Mike Lockwood            HeadsetClientService service = getService();
220aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta            if (service == null) {
221aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta                return false;
222aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta            }
223aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta            return service.disconnect(device);
224aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta        }
225aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta
226aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta        @Override
227aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta        public List<BluetoothDevice> getConnectedDevices() {
2288d536f3db19e8ea7426e98e470dc15d10ecbae87Mike Lockwood            HeadsetClientService service = getService();
229aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta            if (service == null) {
230aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta                return new ArrayList<BluetoothDevice>(0);
231aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta            }
232aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta            return service.getConnectedDevices();
233aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta        }
234aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta
235aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta        @Override
236aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta        public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
2378d536f3db19e8ea7426e98e470dc15d10ecbae87Mike Lockwood            HeadsetClientService service = getService();
238aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta            if (service == null) {
239aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta                return new ArrayList<BluetoothDevice>(0);
240aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta            }
241aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta            return service.getDevicesMatchingConnectionStates(states);
242aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta        }
243aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta
244aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta        @Override
245aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta        public int getConnectionState(BluetoothDevice device) {
2468d536f3db19e8ea7426e98e470dc15d10ecbae87Mike Lockwood            HeadsetClientService service = getService();
247aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta            if (service == null) {
248aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta                return BluetoothProfile.STATE_DISCONNECTED;
249aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta            }
250aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta            return service.getConnectionState(device);
251aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta        }
252aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta
253aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta        @Override
254aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta        public boolean setPriority(BluetoothDevice device, int priority) {
2558d536f3db19e8ea7426e98e470dc15d10ecbae87Mike Lockwood            HeadsetClientService service = getService();
256aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta            if (service == null) {
257aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta                return false;
258aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta            }
259aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta            return service.setPriority(device, priority);
260aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta        }
261aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta
262aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta        @Override
263aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta        public int getPriority(BluetoothDevice device) {
2648d536f3db19e8ea7426e98e470dc15d10ecbae87Mike Lockwood            HeadsetClientService service = getService();
265aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta            if (service == null) {
266aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta                return BluetoothProfile.PRIORITY_UNDEFINED;
267aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta            }
268aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta            return service.getPriority(device);
269aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta        }
270aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta
271aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta        @Override
272aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta        public boolean startVoiceRecognition(BluetoothDevice device) {
2738d536f3db19e8ea7426e98e470dc15d10ecbae87Mike Lockwood            HeadsetClientService service = getService();
274aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta            if (service == null) {
275aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta                return false;
276aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta            }
277aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta            return service.startVoiceRecognition(device);
278aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta        }
279aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta
280aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta        @Override
281aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta        public boolean stopVoiceRecognition(BluetoothDevice device) {
2828d536f3db19e8ea7426e98e470dc15d10ecbae87Mike Lockwood            HeadsetClientService service = getService();
283aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta            if (service == null) {
284aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta                return false;
285aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta            }
286aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta            return service.stopVoiceRecognition(device);
287aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta        }
288aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta
289aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta        @Override
290aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta        public int getAudioState(BluetoothDevice device) {
2918d536f3db19e8ea7426e98e470dc15d10ecbae87Mike Lockwood            HeadsetClientService service = getService();
292aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta            if (service == null) {
2938d536f3db19e8ea7426e98e470dc15d10ecbae87Mike Lockwood                return BluetoothHeadsetClient.STATE_AUDIO_DISCONNECTED;
294aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta            }
295aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta            return service.getAudioState(device);
296aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta        }
297aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta
298aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta        @Override
29984f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal        public void setAudioRouteAllowed(BluetoothDevice device, boolean allowed) {
30084f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal            Log.e(TAG, "setAudioRouteAllowed API not supported");
30122bb39444356637fbcabd4acf04f2fdb9e3da177Bryce Lee        }
30222bb39444356637fbcabd4acf04f2fdb9e3da177Bryce Lee
30322bb39444356637fbcabd4acf04f2fdb9e3da177Bryce Lee        @Override
30484f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal        public boolean getAudioRouteAllowed(BluetoothDevice device) {
30584f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal            Log.e(TAG, "getAudioRouteAllowed API not supported");
30622bb39444356637fbcabd4acf04f2fdb9e3da177Bryce Lee            return false;
30722bb39444356637fbcabd4acf04f2fdb9e3da177Bryce Lee        }
30822bb39444356637fbcabd4acf04f2fdb9e3da177Bryce Lee
30922bb39444356637fbcabd4acf04f2fdb9e3da177Bryce Lee        @Override
31084f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal        public boolean connectAudio(BluetoothDevice device) {
31184f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal            Log.e(TAG, "connectAudio API not supported");
31284f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal            return false;
313aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta        }
314aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta
315aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta        @Override
31684f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal        public boolean disconnectAudio(BluetoothDevice device) {
31784f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal            Log.e(TAG, "disconnectAudio API not supported");
31884f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal            return false;
319aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta        }
320aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta
321aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta        @Override
322aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta        public boolean acceptCall(BluetoothDevice device, int flag) {
3238d536f3db19e8ea7426e98e470dc15d10ecbae87Mike Lockwood            HeadsetClientService service = getService();
324aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta            if (service == null) {
325aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta                return false;
326aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta            }
327aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta            return service.acceptCall(device, flag);
328aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta        }
329aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta
330aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta        @Override
331aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta        public boolean rejectCall(BluetoothDevice device) {
3328d536f3db19e8ea7426e98e470dc15d10ecbae87Mike Lockwood            HeadsetClientService service = getService();
333aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta            if (service == null) {
334aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta                return false;
335aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta            }
336aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta            return service.rejectCall(device);
337aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta        }
338aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta
339aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta        @Override
340aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta        public boolean holdCall(BluetoothDevice device) {
3418d536f3db19e8ea7426e98e470dc15d10ecbae87Mike Lockwood            HeadsetClientService service = getService();
342aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta            if (service == null) {
343aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta                return false;
344aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta            }
345aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta            return service.holdCall(device);
346aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta        }
347aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta
348aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta        @Override
349ca6e7c65b87c52f5e81b273d7ad979a81b52f991Sanket Agarwal        public boolean terminateCall(BluetoothDevice device, BluetoothHeadsetClientCall call) {
3508d536f3db19e8ea7426e98e470dc15d10ecbae87Mike Lockwood            HeadsetClientService service = getService();
351aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta            if (service == null) {
3522a32ce991573191acde4d5d2f875658ada301874Sanket Agarwal                Log.w(TAG, "service is null");
353aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta                return false;
354aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta            }
355edb38dfa0ab86e2e747a6d5799138c8445013789Sanket Agarwal            return service.terminateCall(device, call != null ? call.getUUID() : null);
356aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta        }
357aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta
358aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta        @Override
359aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta        public boolean explicitCallTransfer(BluetoothDevice device) {
3608d536f3db19e8ea7426e98e470dc15d10ecbae87Mike Lockwood            HeadsetClientService service = getService();
361aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta            if (service == null) {
362aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta                return false;
363aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta            }
364aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta            return service.explicitCallTransfer(device);
365aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta        }
366aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta
367aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta        @Override
368aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta        public boolean enterPrivateMode(BluetoothDevice device, int index) {
3698d536f3db19e8ea7426e98e470dc15d10ecbae87Mike Lockwood            HeadsetClientService service = getService();
370aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta            if (service == null) {
371aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta                return false;
372aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta            }
373aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta            return service.enterPrivateMode(device, index);
374aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta        }
375aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta
376aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta        @Override
377ca6e7c65b87c52f5e81b273d7ad979a81b52f991Sanket Agarwal        public BluetoothHeadsetClientCall dial(BluetoothDevice device, String number) {
3788d536f3db19e8ea7426e98e470dc15d10ecbae87Mike Lockwood            HeadsetClientService service = getService();
379aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta            if (service == null) {
380ca6e7c65b87c52f5e81b273d7ad979a81b52f991Sanket Agarwal                return null;
381aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta            }
382aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta            return service.dial(device, number);
383aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta        }
384aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta
385aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta        @Override
3868d536f3db19e8ea7426e98e470dc15d10ecbae87Mike Lockwood        public List<BluetoothHeadsetClientCall> getCurrentCalls(BluetoothDevice device) {
3878d536f3db19e8ea7426e98e470dc15d10ecbae87Mike Lockwood            HeadsetClientService service = getService();
388aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta            if (service == null) {
38984f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal                return new ArrayList<BluetoothHeadsetClientCall>();
390aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta            }
391aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta            return service.getCurrentCalls(device);
392aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta        }
393aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta
394aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta        @Override
395aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta        public boolean sendDTMF(BluetoothDevice device, byte code) {
3968d536f3db19e8ea7426e98e470dc15d10ecbae87Mike Lockwood            HeadsetClientService service = getService();
397aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta            if (service == null) {
398aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta                return false;
399aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta            }
400aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta            return service.sendDTMF(device, code);
401aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta        }
402aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta
403aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta        @Override
404aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta        public boolean getLastVoiceTagNumber(BluetoothDevice device) {
4058d536f3db19e8ea7426e98e470dc15d10ecbae87Mike Lockwood            HeadsetClientService service = getService();
406aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta            if (service == null) {
407aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta                return false;
408aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta            }
409aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta            return service.getLastVoiceTagNumber(device);
410aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta        }
411aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta
412aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta        @Override
413aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta        public Bundle getCurrentAgEvents(BluetoothDevice device) {
4148d536f3db19e8ea7426e98e470dc15d10ecbae87Mike Lockwood            HeadsetClientService service = getService();
415aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta            if (service == null) {
416aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta                return null;
417aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta            }
418aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta            return service.getCurrentAgEvents(device);
419aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta        }
420aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta
421aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta        @Override
422aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta        public Bundle getCurrentAgFeatures(BluetoothDevice device) {
4238d536f3db19e8ea7426e98e470dc15d10ecbae87Mike Lockwood            HeadsetClientService service = getService();
424aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta            if (service == null) {
425aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta                return null;
426aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta            }
427aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta            return service.getCurrentAgFeatures(device);
428aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta        }
429aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta    };
430aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta
431aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta    // API methods
4328d536f3db19e8ea7426e98e470dc15d10ecbae87Mike Lockwood    public static synchronized HeadsetClientService getHeadsetClientService() {
4338d536f3db19e8ea7426e98e470dc15d10ecbae87Mike Lockwood        if (sHeadsetClientService != null && sHeadsetClientService.isAvailable()) {
434aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta            if (DBG) {
4358d536f3db19e8ea7426e98e470dc15d10ecbae87Mike Lockwood                Log.d(TAG, "getHeadsetClientService(): returning " + sHeadsetClientService);
436aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta            }
4378d536f3db19e8ea7426e98e470dc15d10ecbae87Mike Lockwood            return sHeadsetClientService;
438aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta        }
439aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta        if (DBG) {
4408d536f3db19e8ea7426e98e470dc15d10ecbae87Mike Lockwood            if (sHeadsetClientService == null) {
4418d536f3db19e8ea7426e98e470dc15d10ecbae87Mike Lockwood                Log.d(TAG, "getHeadsetClientService(): service is NULL");
4428d536f3db19e8ea7426e98e470dc15d10ecbae87Mike Lockwood            } else if (!(sHeadsetClientService.isAvailable())) {
4438d536f3db19e8ea7426e98e470dc15d10ecbae87Mike Lockwood                Log.d(TAG, "getHeadsetClientService(): service is not available");
444aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta            }
445aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta        }
446aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta        return null;
447aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta    }
448aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta
4498d536f3db19e8ea7426e98e470dc15d10ecbae87Mike Lockwood    private static synchronized void setHeadsetClientService(HeadsetClientService instance) {
450aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta        if (instance != null && instance.isAvailable()) {
451aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta            if (DBG) {
4528d536f3db19e8ea7426e98e470dc15d10ecbae87Mike Lockwood                Log.d(TAG, "setHeadsetClientService(): set to: " + sHeadsetClientService);
453aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta            }
4548d536f3db19e8ea7426e98e470dc15d10ecbae87Mike Lockwood            sHeadsetClientService = instance;
455aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta        } else {
456aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta            if (DBG) {
4578d536f3db19e8ea7426e98e470dc15d10ecbae87Mike Lockwood                if (sHeadsetClientService == null) {
4588d536f3db19e8ea7426e98e470dc15d10ecbae87Mike Lockwood                    Log.d(TAG, "setHeadsetClientService(): service not available");
4598d536f3db19e8ea7426e98e470dc15d10ecbae87Mike Lockwood                } else if (!sHeadsetClientService.isAvailable()) {
4608d536f3db19e8ea7426e98e470dc15d10ecbae87Mike Lockwood                    Log.d(TAG, "setHeadsetClientService(): service is cleaning up");
461aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta                }
462aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta            }
463aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta        }
464aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta    }
465aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta
4668d536f3db19e8ea7426e98e470dc15d10ecbae87Mike Lockwood    private static synchronized void clearHeadsetClientService() {
4678d536f3db19e8ea7426e98e470dc15d10ecbae87Mike Lockwood        sHeadsetClientService = null;
468aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta    }
469aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta
470aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta    public boolean connect(BluetoothDevice device) {
471aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta        enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
472aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta                "Need BLUETOOTH ADMIN permission");
47384f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal        if (DBG) {
47484f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal            Log.d(TAG, "connect " + device);
47584f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal        }
47684f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal        HeadsetClientStateMachine sm = getStateMachine(device);
47784f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal        if (sm == null) {
47884f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal            Log.e(TAG, "Cannot allocate SM for device " + device);
479aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta            return false;
480aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta        }
481aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta
48284f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal        if (getPriority(device) == BluetoothProfile.PRIORITY_OFF) {
48384f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal            Log.w(TAG, "Connection not allowed: <" + device.getAddress() + "> is PRIORITY_OFF");
484aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta            return false;
485aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta        }
486aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta
48784f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal        sm.sendMessage(HeadsetClientStateMachine.CONNECT, device);
488aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta        return true;
489aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta    }
490aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta
491aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta    boolean disconnect(BluetoothDevice device) {
492aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta        enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
493aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta                "Need BLUETOOTH ADMIN permission");
49484f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal        HeadsetClientStateMachine sm = getStateMachine(device);
49584f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal        if (sm == null) {
49684f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal            Log.e(TAG, "Cannot allocate SM for device " + device);
49784f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal            return false;
49884f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal        }
49984f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal
50084f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal        int connectionState = sm.getConnectionState(device);
501aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta        if (connectionState != BluetoothProfile.STATE_CONNECTED &&
502aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta                connectionState != BluetoothProfile.STATE_CONNECTING) {
503aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta            return false;
504aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta        }
505aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta
50684f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal        sm.sendMessage(HeadsetClientStateMachine.DISCONNECT, device);
507aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta        return true;
508aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta    }
509aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta
51084f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal    public synchronized List<BluetoothDevice> getConnectedDevices() {
511aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta        enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
51284f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal
51384f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal        ArrayList<BluetoothDevice> connectedDevices = new ArrayList<>();
51484f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal        for (BluetoothDevice bd : mStateMachineMap.keySet()) {
51584f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal            HeadsetClientStateMachine sm = mStateMachineMap.get(bd);
51684f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal            if (sm != null && sm.getConnectionState(bd) == BluetoothProfile.STATE_CONNECTED) {
51784f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal                connectedDevices.add(bd);
51884f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal            }
51984f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal        }
52084f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal        return connectedDevices;
521aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta    }
522aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta
52384f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal    private synchronized List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
524aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta        enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
52584f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal        List<BluetoothDevice> devices = new ArrayList<BluetoothDevice>();
52684f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal        for (BluetoothDevice bd : mStateMachineMap.keySet()) {
52784f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal            for (int state : states) {
52884f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal                HeadsetClientStateMachine sm = mStateMachineMap.get(bd);
52984f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal                if (sm != null && sm.getConnectionState(bd) == state) {
53084f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal                    devices.add(bd);
53184f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal                }
53284f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal            }
53384f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal        }
53484f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal        return devices;
535aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta    }
536aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta
53784f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal    private synchronized int getConnectionState(BluetoothDevice device) {
538aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta        enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
53984f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal        HeadsetClientStateMachine sm = mStateMachineMap.get(device);
54084f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal        if (sm != null) {
54184f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal            return sm.getConnectionState(device);
54284f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal        }
54384f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal        return BluetoothProfile.STATE_DISCONNECTED;
544aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta    }
545aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta
546aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta    public boolean setPriority(BluetoothDevice device, int priority) {
547aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta        enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
548aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta                "Need BLUETOOTH_ADMIN permission");
549aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta        Settings.Global.putInt(getContentResolver(),
550aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta                Settings.Global.getBluetoothHeadsetPriorityKey(device.getAddress()),
551aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta                priority);
552aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta        if (DBG) {
553aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta            Log.d(TAG, "Saved priority " + device + " = " + priority);
554aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta        }
555aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta        return true;
556aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta    }
557aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta
558aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta    public int getPriority(BluetoothDevice device) {
559aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta        enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
560aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta                "Need BLUETOOTH_ADMIN permission");
561aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta        int priority = Settings.Global.getInt(getContentResolver(),
562aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta                Settings.Global.getBluetoothHeadsetPriorityKey(device.getAddress()),
563aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta                BluetoothProfile.PRIORITY_UNDEFINED);
564aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta        return priority;
565aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta    }
566aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta
567aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta    boolean startVoiceRecognition(BluetoothDevice device) {
56884f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal        Log.e(TAG, "startVoiceRecognition API not available");
569aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta        return false;
570aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta    }
571aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta
57284f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal    boolean stopVoiceRecognition(BluetoothDevice device) {
57384f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal        Log.e(TAG, "stopVoiceRecognition API not available");
574aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta        return false;
575aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta    }
576aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta
577aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta    int getAudioState(BluetoothDevice device) {
57884f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal        HeadsetClientStateMachine sm = getStateMachine(device);
57984f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal        if (sm == null) {
58084f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal            Log.e(TAG, "Cannot allocate SM for device " + device);
58184f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal            return -1;
58284f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal        }
58322bb39444356637fbcabd4acf04f2fdb9e3da177Bryce Lee
58484f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal        return sm.getAudioState(device);
58522bb39444356637fbcabd4acf04f2fdb9e3da177Bryce Lee    }
58622bb39444356637fbcabd4acf04f2fdb9e3da177Bryce Lee
58784f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal    boolean connectAudio(BluetoothDevice device) {
588aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta        enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, "Need BLUETOOTH_ADMIN permission");
58984f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal        HeadsetClientStateMachine sm = getStateMachine(device);
59084f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal        if (sm == null) {
59184f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal            Log.e(TAG, "Cannot allocate SM for device " + device);
592aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta            return false;
593aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta        }
59484f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal
59584f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal        if (!sm.isConnected()) {
59684f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal            return false;
59784f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal        }
59884f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal        if (sm.isAudioOn()) {
599aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta            return false;
600aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta        }
60184f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal        sm.sendMessage(HeadsetClientStateMachine.CONNECT_AUDIO);
602aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta        return true;
603aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta    }
604aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta
60584f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal    boolean disconnectAudio(BluetoothDevice device) {
606aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta        enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, "Need BLUETOOTH_ADMIN permission");
60784f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal        HeadsetClientStateMachine sm = getStateMachine(device);
60884f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal        if (sm == null) {
60984f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal            Log.e(TAG, "Cannot allocate SM for device " + device);
61084f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal            return false;
61184f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal        }
61284f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal
61384f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal        if (!sm.isAudioOn()) {
614aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta            return false;
615aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta        }
61684f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal        sm.sendMessage(HeadsetClientStateMachine.DISCONNECT_AUDIO);
617aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta        return true;
618aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta    }
619aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta
620aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta    boolean holdCall(BluetoothDevice device) {
621aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta        enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
62284f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal        HeadsetClientStateMachine sm = getStateMachine(device);
62384f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal        if (sm == null) {
62484f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal            Log.e(TAG, "Cannot allocate SM for device " + device);
62584f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal            return false;
62684f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal        }
62784f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal
62884f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal        int connectionState = sm.getConnectionState(device);
629aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta        if (connectionState != BluetoothProfile.STATE_CONNECTED &&
630aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta                connectionState != BluetoothProfile.STATE_CONNECTING) {
631aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta            return false;
632aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta        }
63384f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal        Message msg = sm.obtainMessage(HeadsetClientStateMachine.HOLD_CALL);
63484f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal        sm.sendMessage(msg);
635aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta        return true;
636aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta    }
637aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta
638aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta    boolean acceptCall(BluetoothDevice device, int flag) {
639aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta        enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
64084f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal        HeadsetClientStateMachine sm = getStateMachine(device);
64184f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal        if (sm == null) {
64284f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal            Log.e(TAG, "Cannot allocate SM for device " + device);
64384f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal            return false;
64484f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal        }
64584f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal
64684f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal        int connectionState = sm.getConnectionState(device);
647aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta        if (connectionState != BluetoothProfile.STATE_CONNECTED &&
648aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta                connectionState != BluetoothProfile.STATE_CONNECTING) {
649aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta            return false;
650aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta        }
65184f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal        Message msg = sm.obtainMessage(HeadsetClientStateMachine.ACCEPT_CALL);
652aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta        msg.arg1 = flag;
65384f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal        sm.sendMessage(msg);
654aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta        return true;
655aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta    }
656aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta
657aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta    boolean rejectCall(BluetoothDevice device) {
658aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta        enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
65984f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal        HeadsetClientStateMachine sm = getStateMachine(device);
66084f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal        if (sm == null) {
66184f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal            Log.e(TAG, "Cannot allocate SM for device " + device);
66284f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal            return false;
66384f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal        }
66484f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal
66584f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal        int connectionState = sm.getConnectionState(device);
666aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta        if (connectionState != BluetoothProfile.STATE_CONNECTED &&
667aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta                connectionState != BluetoothProfile.STATE_CONNECTING) {
668aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta            return false;
669aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta        }
670aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta
67184f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal        Message msg = sm.obtainMessage(HeadsetClientStateMachine.REJECT_CALL);
67284f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal        sm.sendMessage(msg);
673aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta        return true;
674aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta    }
675aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta
676ca6e7c65b87c52f5e81b273d7ad979a81b52f991Sanket Agarwal    boolean terminateCall(BluetoothDevice device, UUID uuid) {
677aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta        enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
67884f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal        HeadsetClientStateMachine sm = getStateMachine(device);
67984f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal        if (sm == null) {
68084f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal            Log.e(TAG, "Cannot allocate SM for device " + device);
68184f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal            return false;
68284f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal        }
68384f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal
68484f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal        int connectionState = sm.getConnectionState(device);
685aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta        if (connectionState != BluetoothProfile.STATE_CONNECTED &&
686aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta                connectionState != BluetoothProfile.STATE_CONNECTING) {
687aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta            return false;
688aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta        }
689aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta
69084f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal        Message msg = sm.obtainMessage(HeadsetClientStateMachine.TERMINATE_CALL);
691ca6e7c65b87c52f5e81b273d7ad979a81b52f991Sanket Agarwal        msg.obj = uuid;
69284f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal        sm.sendMessage(msg);
693aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta        return true;
694aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta    }
695aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta
696aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta    boolean enterPrivateMode(BluetoothDevice device, int index) {
697aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta        enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
69884f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal        HeadsetClientStateMachine sm = getStateMachine(device);
69984f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal        if (sm == null) {
70084f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal            Log.e(TAG, "Cannot allocate SM for device " + device);
70184f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal            return false;
70284f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal        }
70384f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal
70484f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal        int connectionState = sm.getConnectionState(device);
705aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta        if (connectionState != BluetoothProfile.STATE_CONNECTED &&
706aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta                connectionState != BluetoothProfile.STATE_CONNECTING) {
707aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta            return false;
708aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta        }
709aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta
71084f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal        Message msg = sm.obtainMessage(HeadsetClientStateMachine.ENTER_PRIVATE_MODE);
711aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta        msg.arg1 = index;
71284f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal        sm.sendMessage(msg);
713aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta        return true;
714aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta    }
715aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta
716ca6e7c65b87c52f5e81b273d7ad979a81b52f991Sanket Agarwal    BluetoothHeadsetClientCall dial(BluetoothDevice device, String number) {
717aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta        enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
71884f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal        HeadsetClientStateMachine sm = getStateMachine(device);
71984f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal        if (sm == null) {
72084f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal            Log.e(TAG, "Cannot allocate SM for device " + device);
72184f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal            return null;
72284f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal        }
72384f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal
72484f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal        int connectionState = sm.getConnectionState(device);
725aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta        if (connectionState != BluetoothProfile.STATE_CONNECTED &&
726aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta                connectionState != BluetoothProfile.STATE_CONNECTING) {
727ca6e7c65b87c52f5e81b273d7ad979a81b52f991Sanket Agarwal            return null;
728aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta        }
729aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta
730ca6e7c65b87c52f5e81b273d7ad979a81b52f991Sanket Agarwal        BluetoothHeadsetClientCall call = new BluetoothHeadsetClientCall(
731ca6e7c65b87c52f5e81b273d7ad979a81b52f991Sanket Agarwal            device, HeadsetClientStateMachine.HF_ORIGINATED_CALL_ID,
732ca6e7c65b87c52f5e81b273d7ad979a81b52f991Sanket Agarwal            BluetoothHeadsetClientCall.CALL_STATE_DIALING, number, false  /* multiparty */,
733ca6e7c65b87c52f5e81b273d7ad979a81b52f991Sanket Agarwal            true  /* outgoing */);
73484f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal        Message msg = sm.obtainMessage(HeadsetClientStateMachine.DIAL_NUMBER);
735ca6e7c65b87c52f5e81b273d7ad979a81b52f991Sanket Agarwal        msg.obj = call;
73684f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal        sm.sendMessage(msg);
737ca6e7c65b87c52f5e81b273d7ad979a81b52f991Sanket Agarwal        return call;
738aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta    }
739aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta
740aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta    public boolean sendDTMF(BluetoothDevice device, byte code) {
741aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta        enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
74284f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal        HeadsetClientStateMachine sm = getStateMachine(device);
74384f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal        if (sm == null) {
74484f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal            Log.e(TAG, "Cannot allocate SM for device " + device);
74584f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal            return false;
74684f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal        }
74784f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal
74884f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal        int connectionState = sm.getConnectionState(device);
749aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta        if (connectionState != BluetoothProfile.STATE_CONNECTED &&
750aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta                connectionState != BluetoothProfile.STATE_CONNECTING) {
751aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta            return false;
752aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta        }
75384f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal        Message msg = sm.obtainMessage(HeadsetClientStateMachine.SEND_DTMF);
754aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta        msg.arg1 = code;
75584f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal        sm.sendMessage(msg);
756aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta        return true;
757aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta    }
758aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta
759aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta    public boolean getLastVoiceTagNumber(BluetoothDevice device) {
76084f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal        return false;
761aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta    }
762aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta
7638d536f3db19e8ea7426e98e470dc15d10ecbae87Mike Lockwood    public List<BluetoothHeadsetClientCall> getCurrentCalls(BluetoothDevice device) {
764aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta        enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
76584f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal        HeadsetClientStateMachine sm = getStateMachine(device);
76684f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal        if (sm == null) {
76784f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal            Log.e(TAG, "Cannot allocate SM for device " + device);
76884f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal            return null;
76984f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal        }
77084f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal
77184f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal        int connectionState = sm.getConnectionState(device);
772aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta        if (connectionState != BluetoothProfile.STATE_CONNECTED) {
773aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta            return null;
774aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta        }
77584f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal        return sm.getCurrentCalls();
776aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta    }
777aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta
778aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta    public boolean explicitCallTransfer(BluetoothDevice device) {
779aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta        enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
78084f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal        HeadsetClientStateMachine sm = getStateMachine(device);
78184f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal        if (sm == null) {
78284f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal            Log.e(TAG, "Cannot allocate SM for device " + device);
78384f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal            return false;
78484f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal        }
78584f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal
78684f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal        int connectionState = sm.getConnectionState(device);
787aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta        if (connectionState != BluetoothProfile.STATE_CONNECTED &&
788aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta                connectionState != BluetoothProfile.STATE_CONNECTING) {
789aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta            return false;
790aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta        }
79184f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal        Message msg = sm.obtainMessage(HeadsetClientStateMachine.EXPLICIT_CALL_TRANSFER);
79284f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal        sm.sendMessage(msg);
793aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta        return true;
794aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta    }
795aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta
796aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta    public Bundle getCurrentAgEvents(BluetoothDevice device) {
797aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta        enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
79884f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal        HeadsetClientStateMachine sm = getStateMachine(device);
79984f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal        if (sm == null) {
80084f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal            Log.e(TAG, "Cannot allocate SM for device " + device);
80184f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal            return null;
80284f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal        }
80384f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal
80484f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal        int connectionState = sm.getConnectionState(device);
805aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta        if (connectionState != BluetoothProfile.STATE_CONNECTED) {
806aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta            return null;
807aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta        }
80884f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal        return sm.getCurrentAgEvents();
809aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta    }
810aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta
811aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta    public Bundle getCurrentAgFeatures(BluetoothDevice device) {
812aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta        enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
81384f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal        HeadsetClientStateMachine sm = getStateMachine(device);
81484f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal        if (sm == null) {
81584f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal            Log.e(TAG, "Cannot allocate SM for device " + device);
81684f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal            return null;
81784f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal        }
81884f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal        int connectionState = sm.getConnectionState(device);
819aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta        if (connectionState != BluetoothProfile.STATE_CONNECTED) {
820aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta            return null;
821aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta        }
82284f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal        return sm.getCurrentAgFeatures();
82384f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal    }
82484f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal
82584f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal    // Handle messages from native (JNI) to java
82684f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal    public void messageFromNative(StackEvent stackEvent) {
82784f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal        HeadsetClientStateMachine sm = getStateMachine(stackEvent.device);
82884f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal        if (sm == null) {
82984f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal            Log.w(TAG, "No SM found for event " + stackEvent);
83084f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal        }
83184f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal
83284f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal        sm.sendMessage(StackEvent.STACK_EVENT, stackEvent);
83384f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal    }
83484f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal
83584f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal    // State machine management
83684f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal    private synchronized HeadsetClientStateMachine getStateMachine(BluetoothDevice device) {
83784f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal        if (device == null) {
83884f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal            Log.e(TAG, "getStateMachine failed: Device cannot be null");
83984f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal            return null;
84084f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal        }
84184f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal
84284f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal        HeadsetClientStateMachine sm = mStateMachineMap.get(device);
84384f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal        if (sm != null) {
84484f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal            if (DBG) {
84584f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal                Log.d(TAG, "Found SM for device " + device);
84684f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal            }
84784f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal            return sm;
84884f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal        }
84984f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal
85084f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal        // There is a possibility of a DOS attack if someone populates here with a lot of fake
85184f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal        // BluetoothAddresses. If it so happens instead of blowing up we can atleast put a limit on
85284f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal        // how long the attack would survive
85384f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal        if (mStateMachineMap.keySet().size() > MAX_STATE_MACHINES_POSSIBLE) {
85484f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal            Log.e(TAG, "Max state machines reached, possible DOS attack " +
85584f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal                MAX_STATE_MACHINES_POSSIBLE);
85684f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal            return null;
85784f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal        }
85884f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal
85984f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal        // Allocate a new SM
86084f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal        Log.d(TAG, "Creating a new state machine");
86184f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal        sm = mSmFactory.make(this, mSmThread);
86284f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal        mStateMachineMap.put(device, sm);
86384f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal        return sm;
864aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta    }
865838949d46a4cc054985a8cfd682004f8dd6d3bbbMike Lockwood
866838949d46a4cc054985a8cfd682004f8dd6d3bbbMike Lockwood    @Override
86784f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal    public synchronized void dump(StringBuilder sb) {
868838949d46a4cc054985a8cfd682004f8dd6d3bbbMike Lockwood        super.dump(sb);
86984f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal        for (HeadsetClientStateMachine sm : mStateMachineMap.values()) {
87084f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal            if (sm != null) {
87184f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal                println(sb, "State machine:");
87284f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal                println(sb, "=============");
87384f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal                sm.dump(sb);
87484f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal            }
875838949d46a4cc054985a8cfd682004f8dd6d3bbbMike Lockwood        }
876838949d46a4cc054985a8cfd682004f8dd6d3bbbMike Lockwood    }
87784f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal
87884f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal    // For testing
87984f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal    protected synchronized Map<BluetoothDevice, HeadsetClientStateMachine> getStateMachineMap() {
88084f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal        return mStateMachineMap;
88184f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal    }
88284f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal
88384f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal    protected void setSMFactory(HeadsetClientStateMachineFactory factory) {
88484f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal        mSmFactory = factory;
88584f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal    }
886aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta}
887