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;
6393d129a2679021c3275ba940405ba2609853b9eeSanket Agarwal    private AudioManager mAudioManager = null;
6484f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal    // Maxinum number of devices we can try connecting to in one session
6584f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal    private static final int MAX_STATE_MACHINES_POSSIBLE = 100;
66aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta
67a8fc504fed7dcb5dc074980b9d4cfa7a56445424Sanket Agarwal    public static String HFP_CLIENT_STOP_TAG = "hfp_client_stop_tag";
68a8fc504fed7dcb5dc074980b9d4cfa7a56445424Sanket Agarwal
6984f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal    static {
70727dc7918318d01b5f2308e55038b7fcba7765f8Sanket Agarwal        NativeInterface.classInitNative();
7184f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal    }
7284f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal
73aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta    @Override
74aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta    protected String getName() {
75aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta        return TAG;
76aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta    }
77aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta
78aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta    @Override
79aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta    public IProfileServiceBinder initBinder() {
808d536f3db19e8ea7426e98e470dc15d10ecbae87Mike Lockwood        return new BluetoothHeadsetClientBinder(this);
81aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta    }
82aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta
83aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta    @Override
8484f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal    protected synchronized boolean start() {
85727dc7918318d01b5f2308e55038b7fcba7765f8Sanket Agarwal        if (DBG) {
86727dc7918318d01b5f2308e55038b7fcba7765f8Sanket Agarwal            Log.d(TAG, "start()");
87727dc7918318d01b5f2308e55038b7fcba7765f8Sanket Agarwal        }
88727dc7918318d01b5f2308e55038b7fcba7765f8Sanket Agarwal        // Setup the JNI service
89727dc7918318d01b5f2308e55038b7fcba7765f8Sanket Agarwal        NativeInterface.initializeNative();
9093d129a2679021c3275ba940405ba2609853b9eeSanket Agarwal        mAudioManager = (AudioManager) getSystemService(Context.AUDIO_SERVICE);
91727dc7918318d01b5f2308e55038b7fcba7765f8Sanket Agarwal
9284f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal        mSmFactory = new HeadsetClientStateMachineFactory();
9384f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal        mStateMachineMap.clear();
9484f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal
95aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta        IntentFilter filter = new IntentFilter(AudioManager.VOLUME_CHANGED_ACTION);
96aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta        try {
97aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta            registerReceiver(mBroadcastReceiver, filter);
98aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta        } catch (Exception e) {
99aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta            Log.w(TAG, "Unable to register broadcat receiver", e);
100aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta        }
1018d536f3db19e8ea7426e98e470dc15d10ecbae87Mike Lockwood        setHeadsetClientService(this);
10284f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal        mNativeInterface = new NativeInterface();
103a8fc504fed7dcb5dc074980b9d4cfa7a56445424Sanket Agarwal
104a8fc504fed7dcb5dc074980b9d4cfa7a56445424Sanket Agarwal        // Start the HfpClientConnectionService to create connection with telecom when HFP
105a8fc504fed7dcb5dc074980b9d4cfa7a56445424Sanket Agarwal        // connection is available.
106a8fc504fed7dcb5dc074980b9d4cfa7a56445424Sanket Agarwal        Intent startIntent = new Intent(this, HfpClientConnectionService.class);
107a8fc504fed7dcb5dc074980b9d4cfa7a56445424Sanket Agarwal        startService(startIntent);
108a8fc504fed7dcb5dc074980b9d4cfa7a56445424Sanket Agarwal
10984f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal        // Create the thread on which all State Machines will run
11084f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal        mSmThread = new HandlerThread("HeadsetClient.SM");
11184f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal        mSmThread.start();
11284f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal        NativeInterface.initializeNative();
11384f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal
114aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta        return true;
115aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta    }
116aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta
117aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta    @Override
11884f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal    protected synchronized boolean stop() {
119aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta        try {
120aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta            unregisterReceiver(mBroadcastReceiver);
121aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta        } catch (Exception e) {
122aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta            Log.w(TAG, "Unable to unregister broadcast receiver", e);
123aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta        }
124562547733c2dd517f3bdd30f29bfe43732fe9a13Chenjie Luo
12584f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal        for (Iterator<Map.Entry<BluetoothDevice, HeadsetClientStateMachine>> it =
12684f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal                mStateMachineMap.entrySet().iterator(); it.hasNext(); ) {
12784f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal            HeadsetClientStateMachine sm =
12884f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal                mStateMachineMap.get((BluetoothDevice) it.next().getKey());
12984f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal            sm.doQuit();
13084f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal            it.remove();
131bebc4dd9edb40710d9588b4a2010eef227756a8eLianchao Song        }
132a8fc504fed7dcb5dc074980b9d4cfa7a56445424Sanket Agarwal
133a8fc504fed7dcb5dc074980b9d4cfa7a56445424Sanket Agarwal        // Stop the HfpClientConnectionService.
134a8fc504fed7dcb5dc074980b9d4cfa7a56445424Sanket Agarwal        Intent stopIntent = new Intent(this, HfpClientConnectionService.class);
135a8fc504fed7dcb5dc074980b9d4cfa7a56445424Sanket Agarwal        stopIntent.putExtra(HFP_CLIENT_STOP_TAG, true);
136a8fc504fed7dcb5dc074980b9d4cfa7a56445424Sanket Agarwal        startService(stopIntent);
13784f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal        mNativeInterface = null;
13884f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal
13984f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal        // Stop the handler thread
14084f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal        mSmThread.quit();
14184f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal        mSmThread = null;
14284f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal
14384f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal        NativeInterface.cleanupNative();
14484f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal
145aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta        return true;
146aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta    }
147aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta
148aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta    @Override
149aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta    protected boolean cleanup() {
15084f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal        HeadsetClientStateMachine.cleanup();
1518d536f3db19e8ea7426e98e470dc15d10ecbae87Mike Lockwood        clearHeadsetClientService();
152aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta        return true;
153aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta    }
154aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta
155aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta    private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
156aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta        @Override
157aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta        public void onReceive(Context context, Intent intent) {
158aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta            String action = intent.getAction();
159aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta
1603076d2acccf5c3db0db19b622f062e259be39f3aSanket Agarwal            // We handle the volume changes for Voice calls here since HFP audio volume control does
16193d129a2679021c3275ba940405ba2609853b9eeSanket Agarwal            // not go through audio manager (audio mixer). see
1623076d2acccf5c3db0db19b622f062e259be39f3aSanket Agarwal            // ({@link HeadsetClientStateMachine#SET_SPEAKER_VOLUME} in
1633076d2acccf5c3db0db19b622f062e259be39f3aSanket Agarwal            // {@link HeadsetClientStateMachine} for details.
164aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta            if (action.equals(AudioManager.VOLUME_CHANGED_ACTION)) {
1653d35808e7d5c704b4cc4f7fe0dc1d8cafc3f1fdaJoseph Pirozzo                if (DBG) {
1663d35808e7d5c704b4cc4f7fe0dc1d8cafc3f1fdaJoseph Pirozzo                    Log.d(TAG,
1673d35808e7d5c704b4cc4f7fe0dc1d8cafc3f1fdaJoseph Pirozzo                            "Volume changed for stream: "
1683d35808e7d5c704b4cc4f7fe0dc1d8cafc3f1fdaJoseph Pirozzo                                    + intent.getExtra(AudioManager.EXTRA_VOLUME_STREAM_TYPE));
1693d35808e7d5c704b4cc4f7fe0dc1d8cafc3f1fdaJoseph Pirozzo                }
170aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta                int streamType = intent.getIntExtra(AudioManager.EXTRA_VOLUME_STREAM_TYPE, -1);
1713076d2acccf5c3db0db19b622f062e259be39f3aSanket Agarwal                if (streamType == AudioManager.STREAM_VOICE_CALL) {
172aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta                    int streamValue = intent
173aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta                            .getIntExtra(AudioManager.EXTRA_VOLUME_STREAM_VALUE, -1);
17493d129a2679021c3275ba940405ba2609853b9eeSanket Agarwal                    int hfVol = HeadsetClientStateMachine.amToHfVol(streamValue);
1753d35808e7d5c704b4cc4f7fe0dc1d8cafc3f1fdaJoseph Pirozzo                    if (DBG) {
1763d35808e7d5c704b4cc4f7fe0dc1d8cafc3f1fdaJoseph Pirozzo                        Log.d(TAG,
1773d35808e7d5c704b4cc4f7fe0dc1d8cafc3f1fdaJoseph Pirozzo                                "Setting volume to audio manager: " + streamValue
1783d35808e7d5c704b4cc4f7fe0dc1d8cafc3f1fdaJoseph Pirozzo                                        + " hands free: " + hfVol);
1793d35808e7d5c704b4cc4f7fe0dc1d8cafc3f1fdaJoseph Pirozzo                    }
18093d129a2679021c3275ba940405ba2609853b9eeSanket Agarwal                    mAudioManager.setParameters("hfp_volume=" + hfVol);
1813d35808e7d5c704b4cc4f7fe0dc1d8cafc3f1fdaJoseph Pirozzo                    synchronized (this) {
1823d35808e7d5c704b4cc4f7fe0dc1d8cafc3f1fdaJoseph Pirozzo                        for (HeadsetClientStateMachine sm : mStateMachineMap.values()) {
1833d35808e7d5c704b4cc4f7fe0dc1d8cafc3f1fdaJoseph Pirozzo                            if (sm != null) {
1843d35808e7d5c704b4cc4f7fe0dc1d8cafc3f1fdaJoseph Pirozzo                                sm.sendMessage(
1853d35808e7d5c704b4cc4f7fe0dc1d8cafc3f1fdaJoseph Pirozzo                                        HeadsetClientStateMachine.SET_SPEAKER_VOLUME, streamValue);
1863d35808e7d5c704b4cc4f7fe0dc1d8cafc3f1fdaJoseph Pirozzo                            }
1873d35808e7d5c704b4cc4f7fe0dc1d8cafc3f1fdaJoseph Pirozzo                        }
1883d35808e7d5c704b4cc4f7fe0dc1d8cafc3f1fdaJoseph Pirozzo                    }
189aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta                }
190aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta            }
191aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta        }
192aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta    };
193aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta
194aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta    /**
195aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta     * Handlers for incoming service calls
196aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta     */
1978d536f3db19e8ea7426e98e470dc15d10ecbae87Mike Lockwood    private static class BluetoothHeadsetClientBinder extends IBluetoothHeadsetClient.Stub
198aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta            implements IProfileServiceBinder {
1998d536f3db19e8ea7426e98e470dc15d10ecbae87Mike Lockwood        private HeadsetClientService mService;
200aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta
2018d536f3db19e8ea7426e98e470dc15d10ecbae87Mike Lockwood        public BluetoothHeadsetClientBinder(HeadsetClientService svc) {
202aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta            mService = svc;
203aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta        }
204aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta
205aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta        @Override
206aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta        public boolean cleanup() {
207aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta            mService = null;
208aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta            return true;
209aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta        }
210aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta
2118d536f3db19e8ea7426e98e470dc15d10ecbae87Mike Lockwood        private HeadsetClientService getService() {
212aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta            if (!Utils.checkCaller()) {
2138d536f3db19e8ea7426e98e470dc15d10ecbae87Mike Lockwood                Log.w(TAG, "HeadsetClient call not allowed for non-active user");
214aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta                return null;
215aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta            }
216aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta
217aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta            if (mService != null && mService.isAvailable()) {
218aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta                return mService;
219aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta            }
22033acd2208455171249084dd86ae0ed95db1e4c49Wei Liu
22133acd2208455171249084dd86ae0ed95db1e4c49Wei Liu            Log.e(TAG, "HeadsetClientService is not available.");
222aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta            return null;
223aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta        }
224aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta
225aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta        @Override
226aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta        public boolean connect(BluetoothDevice device) {
2278d536f3db19e8ea7426e98e470dc15d10ecbae87Mike Lockwood            HeadsetClientService service = getService();
228aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta            if (service == null) {
229aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta                return false;
230aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta            }
231aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta            return service.connect(device);
232aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta        }
233aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta
234aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta        @Override
235aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta        public boolean disconnect(BluetoothDevice device) {
2368d536f3db19e8ea7426e98e470dc15d10ecbae87Mike Lockwood            HeadsetClientService service = getService();
237aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta            if (service == null) {
238aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta                return false;
239aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta            }
240aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta            return service.disconnect(device);
241aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta        }
242aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta
243aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta        @Override
244aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta        public List<BluetoothDevice> getConnectedDevices() {
2458d536f3db19e8ea7426e98e470dc15d10ecbae87Mike Lockwood            HeadsetClientService service = getService();
246aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta            if (service == null) {
247aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta                return new ArrayList<BluetoothDevice>(0);
248aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta            }
249aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta            return service.getConnectedDevices();
250aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta        }
251aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta
252aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta        @Override
253aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta        public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
2548d536f3db19e8ea7426e98e470dc15d10ecbae87Mike Lockwood            HeadsetClientService service = getService();
255aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta            if (service == null) {
256aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta                return new ArrayList<BluetoothDevice>(0);
257aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta            }
258aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta            return service.getDevicesMatchingConnectionStates(states);
259aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta        }
260aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta
261aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta        @Override
262aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta        public int getConnectionState(BluetoothDevice device) {
2638d536f3db19e8ea7426e98e470dc15d10ecbae87Mike Lockwood            HeadsetClientService service = getService();
264aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta            if (service == null) {
265aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta                return BluetoothProfile.STATE_DISCONNECTED;
266aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta            }
267aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta            return service.getConnectionState(device);
268aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta        }
269aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta
270aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta        @Override
271aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta        public boolean setPriority(BluetoothDevice device, int priority) {
2728d536f3db19e8ea7426e98e470dc15d10ecbae87Mike Lockwood            HeadsetClientService service = getService();
273aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta            if (service == null) {
274aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta                return false;
275aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta            }
276aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta            return service.setPriority(device, priority);
277aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta        }
278aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta
279aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta        @Override
280aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta        public int getPriority(BluetoothDevice device) {
2818d536f3db19e8ea7426e98e470dc15d10ecbae87Mike Lockwood            HeadsetClientService service = getService();
282aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta            if (service == null) {
283aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta                return BluetoothProfile.PRIORITY_UNDEFINED;
284aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta            }
285aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta            return service.getPriority(device);
286aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta        }
287aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta
288aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta        @Override
289aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta        public boolean startVoiceRecognition(BluetoothDevice device) {
2908d536f3db19e8ea7426e98e470dc15d10ecbae87Mike Lockwood            HeadsetClientService service = getService();
291aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta            if (service == null) {
292aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta                return false;
293aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta            }
294aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta            return service.startVoiceRecognition(device);
295aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta        }
296aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta
297aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta        @Override
298aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta        public boolean stopVoiceRecognition(BluetoothDevice device) {
2998d536f3db19e8ea7426e98e470dc15d10ecbae87Mike Lockwood            HeadsetClientService service = getService();
300aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta            if (service == null) {
301aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta                return false;
302aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta            }
303aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta            return service.stopVoiceRecognition(device);
304aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta        }
305aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta
306aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta        @Override
307aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta        public int getAudioState(BluetoothDevice device) {
3088d536f3db19e8ea7426e98e470dc15d10ecbae87Mike Lockwood            HeadsetClientService service = getService();
309aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta            if (service == null) {
3108d536f3db19e8ea7426e98e470dc15d10ecbae87Mike Lockwood                return BluetoothHeadsetClient.STATE_AUDIO_DISCONNECTED;
311aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta            }
312aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta            return service.getAudioState(device);
313aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta        }
314aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta
315aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta        @Override
31684f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal        public void setAudioRouteAllowed(BluetoothDevice device, boolean allowed) {
31784f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal            Log.e(TAG, "setAudioRouteAllowed API not supported");
31822bb39444356637fbcabd4acf04f2fdb9e3da177Bryce Lee        }
31922bb39444356637fbcabd4acf04f2fdb9e3da177Bryce Lee
32022bb39444356637fbcabd4acf04f2fdb9e3da177Bryce Lee        @Override
32184f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal        public boolean getAudioRouteAllowed(BluetoothDevice device) {
32284f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal            Log.e(TAG, "getAudioRouteAllowed API not supported");
32322bb39444356637fbcabd4acf04f2fdb9e3da177Bryce Lee            return false;
32422bb39444356637fbcabd4acf04f2fdb9e3da177Bryce Lee        }
32522bb39444356637fbcabd4acf04f2fdb9e3da177Bryce Lee
32622bb39444356637fbcabd4acf04f2fdb9e3da177Bryce Lee        @Override
32784f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal        public boolean connectAudio(BluetoothDevice device) {
3282d9c62894634e9c4aa947ddf2cc50f4482163cf1Sanket Agarwal            HeadsetClientService service = getService();
3292d9c62894634e9c4aa947ddf2cc50f4482163cf1Sanket Agarwal            if (service == null) {
3302d9c62894634e9c4aa947ddf2cc50f4482163cf1Sanket Agarwal                return false;
3312d9c62894634e9c4aa947ddf2cc50f4482163cf1Sanket Agarwal            }
3322d9c62894634e9c4aa947ddf2cc50f4482163cf1Sanket Agarwal            return service.connectAudio(device);
333aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta        }
334aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta
335aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta        @Override
33684f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal        public boolean disconnectAudio(BluetoothDevice device) {
3372d9c62894634e9c4aa947ddf2cc50f4482163cf1Sanket Agarwal            HeadsetClientService service = getService();
3382d9c62894634e9c4aa947ddf2cc50f4482163cf1Sanket Agarwal            if (service == null) {
3392d9c62894634e9c4aa947ddf2cc50f4482163cf1Sanket Agarwal                return false;
3402d9c62894634e9c4aa947ddf2cc50f4482163cf1Sanket Agarwal            }
3412d9c62894634e9c4aa947ddf2cc50f4482163cf1Sanket Agarwal            return service.disconnectAudio(device);
342aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta        }
343aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta
344aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta        @Override
345aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta        public boolean acceptCall(BluetoothDevice device, int flag) {
3468d536f3db19e8ea7426e98e470dc15d10ecbae87Mike Lockwood            HeadsetClientService service = getService();
347aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta            if (service == null) {
348aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta                return false;
349aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta            }
350aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta            return service.acceptCall(device, flag);
351aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta        }
352aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta
353aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta        @Override
354aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta        public boolean rejectCall(BluetoothDevice device) {
3558d536f3db19e8ea7426e98e470dc15d10ecbae87Mike Lockwood            HeadsetClientService service = getService();
356aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta            if (service == null) {
357aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta                return false;
358aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta            }
359aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta            return service.rejectCall(device);
360aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta        }
361aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta
362aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta        @Override
363aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta        public boolean holdCall(BluetoothDevice device) {
3648d536f3db19e8ea7426e98e470dc15d10ecbae87Mike Lockwood            HeadsetClientService service = getService();
365aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta            if (service == null) {
366aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta                return false;
367aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta            }
368aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta            return service.holdCall(device);
369aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta        }
370aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta
371aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta        @Override
372ca6e7c65b87c52f5e81b273d7ad979a81b52f991Sanket Agarwal        public boolean terminateCall(BluetoothDevice device, BluetoothHeadsetClientCall call) {
3738d536f3db19e8ea7426e98e470dc15d10ecbae87Mike Lockwood            HeadsetClientService service = getService();
374aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta            if (service == null) {
3752a32ce991573191acde4d5d2f875658ada301874Sanket Agarwal                Log.w(TAG, "service is null");
376aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta                return false;
377aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta            }
378edb38dfa0ab86e2e747a6d5799138c8445013789Sanket Agarwal            return service.terminateCall(device, call != null ? call.getUUID() : null);
379aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta        }
380aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta
381aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta        @Override
382aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta        public boolean explicitCallTransfer(BluetoothDevice device) {
3838d536f3db19e8ea7426e98e470dc15d10ecbae87Mike Lockwood            HeadsetClientService service = getService();
384aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta            if (service == null) {
385aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta                return false;
386aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta            }
387aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta            return service.explicitCallTransfer(device);
388aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta        }
389aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta
390aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta        @Override
391aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta        public boolean enterPrivateMode(BluetoothDevice device, int index) {
3928d536f3db19e8ea7426e98e470dc15d10ecbae87Mike Lockwood            HeadsetClientService service = getService();
393aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta            if (service == null) {
394aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta                return false;
395aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta            }
396aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta            return service.enterPrivateMode(device, index);
397aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta        }
398aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta
399aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta        @Override
400ca6e7c65b87c52f5e81b273d7ad979a81b52f991Sanket Agarwal        public BluetoothHeadsetClientCall dial(BluetoothDevice device, String number) {
4018d536f3db19e8ea7426e98e470dc15d10ecbae87Mike Lockwood            HeadsetClientService service = getService();
402aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta            if (service == null) {
403ca6e7c65b87c52f5e81b273d7ad979a81b52f991Sanket Agarwal                return null;
404aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta            }
405aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta            return service.dial(device, number);
406aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta        }
407aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta
408aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta        @Override
4098d536f3db19e8ea7426e98e470dc15d10ecbae87Mike Lockwood        public List<BluetoothHeadsetClientCall> getCurrentCalls(BluetoothDevice device) {
4108d536f3db19e8ea7426e98e470dc15d10ecbae87Mike Lockwood            HeadsetClientService service = getService();
411aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta            if (service == null) {
41284f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal                return new ArrayList<BluetoothHeadsetClientCall>();
413aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta            }
414aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta            return service.getCurrentCalls(device);
415aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta        }
416aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta
417aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta        @Override
418aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta        public boolean sendDTMF(BluetoothDevice device, byte code) {
4198d536f3db19e8ea7426e98e470dc15d10ecbae87Mike Lockwood            HeadsetClientService service = getService();
420aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta            if (service == null) {
421aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta                return false;
422aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta            }
423aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta            return service.sendDTMF(device, code);
424aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta        }
425aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta
426aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta        @Override
427aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta        public boolean getLastVoiceTagNumber(BluetoothDevice device) {
4288d536f3db19e8ea7426e98e470dc15d10ecbae87Mike Lockwood            HeadsetClientService service = getService();
429aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta            if (service == null) {
430aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta                return false;
431aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta            }
432aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta            return service.getLastVoiceTagNumber(device);
433aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta        }
434aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta
435aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta        @Override
436aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta        public Bundle getCurrentAgEvents(BluetoothDevice device) {
4378d536f3db19e8ea7426e98e470dc15d10ecbae87Mike Lockwood            HeadsetClientService service = getService();
438aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta            if (service == null) {
439aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta                return null;
440aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta            }
441aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta            return service.getCurrentAgEvents(device);
442aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta        }
443aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta
444aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta        @Override
445aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta        public Bundle getCurrentAgFeatures(BluetoothDevice device) {
4468d536f3db19e8ea7426e98e470dc15d10ecbae87Mike Lockwood            HeadsetClientService service = getService();
447aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta            if (service == null) {
448aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta                return null;
449aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta            }
450aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta            return service.getCurrentAgFeatures(device);
451aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta        }
452aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta    };
453aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta
454aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta    // API methods
4558d536f3db19e8ea7426e98e470dc15d10ecbae87Mike Lockwood    public static synchronized HeadsetClientService getHeadsetClientService() {
4568d536f3db19e8ea7426e98e470dc15d10ecbae87Mike Lockwood        if (sHeadsetClientService != null && sHeadsetClientService.isAvailable()) {
457aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta            if (DBG) {
4588d536f3db19e8ea7426e98e470dc15d10ecbae87Mike Lockwood                Log.d(TAG, "getHeadsetClientService(): returning " + sHeadsetClientService);
459aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta            }
4608d536f3db19e8ea7426e98e470dc15d10ecbae87Mike Lockwood            return sHeadsetClientService;
461aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta        }
462aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta        if (DBG) {
4638d536f3db19e8ea7426e98e470dc15d10ecbae87Mike Lockwood            if (sHeadsetClientService == null) {
4648d536f3db19e8ea7426e98e470dc15d10ecbae87Mike Lockwood                Log.d(TAG, "getHeadsetClientService(): service is NULL");
4658d536f3db19e8ea7426e98e470dc15d10ecbae87Mike Lockwood            } else if (!(sHeadsetClientService.isAvailable())) {
4668d536f3db19e8ea7426e98e470dc15d10ecbae87Mike Lockwood                Log.d(TAG, "getHeadsetClientService(): service is not available");
467aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta            }
468aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta        }
469aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta        return null;
470aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta    }
471aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta
4728d536f3db19e8ea7426e98e470dc15d10ecbae87Mike Lockwood    private static synchronized void setHeadsetClientService(HeadsetClientService instance) {
473aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta        if (instance != null && instance.isAvailable()) {
474aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta            if (DBG) {
4758d536f3db19e8ea7426e98e470dc15d10ecbae87Mike Lockwood                Log.d(TAG, "setHeadsetClientService(): set to: " + sHeadsetClientService);
476aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta            }
4778d536f3db19e8ea7426e98e470dc15d10ecbae87Mike Lockwood            sHeadsetClientService = instance;
478aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta        } else {
479aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta            if (DBG) {
4808d536f3db19e8ea7426e98e470dc15d10ecbae87Mike Lockwood                if (sHeadsetClientService == null) {
4818d536f3db19e8ea7426e98e470dc15d10ecbae87Mike Lockwood                    Log.d(TAG, "setHeadsetClientService(): service not available");
4828d536f3db19e8ea7426e98e470dc15d10ecbae87Mike Lockwood                } else if (!sHeadsetClientService.isAvailable()) {
4838d536f3db19e8ea7426e98e470dc15d10ecbae87Mike Lockwood                    Log.d(TAG, "setHeadsetClientService(): service is cleaning up");
484aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta                }
485aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta            }
486aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta        }
487aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta    }
488aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta
4898d536f3db19e8ea7426e98e470dc15d10ecbae87Mike Lockwood    private static synchronized void clearHeadsetClientService() {
4908d536f3db19e8ea7426e98e470dc15d10ecbae87Mike Lockwood        sHeadsetClientService = null;
491aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta    }
492aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta
493aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta    public boolean connect(BluetoothDevice device) {
494aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta        enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
495aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta                "Need BLUETOOTH ADMIN permission");
49684f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal        if (DBG) {
49784f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal            Log.d(TAG, "connect " + device);
49884f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal        }
49984f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal        HeadsetClientStateMachine sm = getStateMachine(device);
50084f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal        if (sm == null) {
50184f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal            Log.e(TAG, "Cannot allocate SM for device " + device);
502aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta            return false;
503aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta        }
504aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta
50584f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal        if (getPriority(device) == BluetoothProfile.PRIORITY_OFF) {
50684f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal            Log.w(TAG, "Connection not allowed: <" + device.getAddress() + "> is PRIORITY_OFF");
507aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta            return false;
508aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta        }
509aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta
51084f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal        sm.sendMessage(HeadsetClientStateMachine.CONNECT, device);
511aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta        return true;
512aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta    }
513aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta
514aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta    boolean disconnect(BluetoothDevice device) {
515aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta        enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
516aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta                "Need BLUETOOTH ADMIN permission");
51784f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal        HeadsetClientStateMachine sm = getStateMachine(device);
51884f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal        if (sm == null) {
51984f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal            Log.e(TAG, "Cannot allocate SM for device " + device);
52084f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal            return false;
52184f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal        }
52284f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal
52384f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal        int connectionState = sm.getConnectionState(device);
524aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta        if (connectionState != BluetoothProfile.STATE_CONNECTED &&
525aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta                connectionState != BluetoothProfile.STATE_CONNECTING) {
526aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta            return false;
527aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta        }
528aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta
52984f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal        sm.sendMessage(HeadsetClientStateMachine.DISCONNECT, device);
530aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta        return true;
531aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta    }
532aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta
53384f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal    public synchronized List<BluetoothDevice> getConnectedDevices() {
534aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta        enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
53584f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal
53684f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal        ArrayList<BluetoothDevice> connectedDevices = new ArrayList<>();
53784f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal        for (BluetoothDevice bd : mStateMachineMap.keySet()) {
53884f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal            HeadsetClientStateMachine sm = mStateMachineMap.get(bd);
53984f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal            if (sm != null && sm.getConnectionState(bd) == BluetoothProfile.STATE_CONNECTED) {
54084f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal                connectedDevices.add(bd);
54184f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal            }
54284f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal        }
54384f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal        return connectedDevices;
544aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta    }
545aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta
54684f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal    private synchronized List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
547aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta        enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
54884f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal        List<BluetoothDevice> devices = new ArrayList<BluetoothDevice>();
54984f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal        for (BluetoothDevice bd : mStateMachineMap.keySet()) {
55084f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal            for (int state : states) {
55184f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal                HeadsetClientStateMachine sm = mStateMachineMap.get(bd);
55284f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal                if (sm != null && sm.getConnectionState(bd) == state) {
55384f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal                    devices.add(bd);
55484f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal                }
55584f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal            }
55684f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal        }
55784f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal        return devices;
558aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta    }
559aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta
56084f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal    private synchronized int getConnectionState(BluetoothDevice device) {
561aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta        enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
56284f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal        HeadsetClientStateMachine sm = mStateMachineMap.get(device);
56384f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal        if (sm != null) {
56484f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal            return sm.getConnectionState(device);
56584f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal        }
56684f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal        return BluetoothProfile.STATE_DISCONNECTED;
567aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta    }
568aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta
569aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta    public boolean setPriority(BluetoothDevice device, int priority) {
570aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta        enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
571aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta                "Need BLUETOOTH_ADMIN permission");
572aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta        Settings.Global.putInt(getContentResolver(),
573aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta                Settings.Global.getBluetoothHeadsetPriorityKey(device.getAddress()),
574aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta                priority);
575aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta        if (DBG) {
576aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta            Log.d(TAG, "Saved priority " + device + " = " + priority);
577aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta        }
578aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta        return true;
579aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta    }
580aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta
581aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta    public int getPriority(BluetoothDevice device) {
582aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta        enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
583aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta                "Need BLUETOOTH_ADMIN permission");
584aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta        int priority = Settings.Global.getInt(getContentResolver(),
585aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta                Settings.Global.getBluetoothHeadsetPriorityKey(device.getAddress()),
586aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta                BluetoothProfile.PRIORITY_UNDEFINED);
587aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta        return priority;
588aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta    }
589aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta
590aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta    boolean startVoiceRecognition(BluetoothDevice device) {
59184f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal        Log.e(TAG, "startVoiceRecognition API not available");
592aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta        return false;
593aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta    }
594aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta
59584f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal    boolean stopVoiceRecognition(BluetoothDevice device) {
59684f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal        Log.e(TAG, "stopVoiceRecognition API not available");
597aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta        return false;
598aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta    }
599aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta
600aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta    int getAudioState(BluetoothDevice device) {
60184f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal        HeadsetClientStateMachine sm = getStateMachine(device);
60284f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal        if (sm == null) {
60384f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal            Log.e(TAG, "Cannot allocate SM for device " + device);
60484f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal            return -1;
60584f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal        }
60622bb39444356637fbcabd4acf04f2fdb9e3da177Bryce Lee
60784f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal        return sm.getAudioState(device);
60822bb39444356637fbcabd4acf04f2fdb9e3da177Bryce Lee    }
60922bb39444356637fbcabd4acf04f2fdb9e3da177Bryce Lee
61084f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal    boolean connectAudio(BluetoothDevice device) {
611aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta        enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, "Need BLUETOOTH_ADMIN permission");
61284f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal        HeadsetClientStateMachine sm = getStateMachine(device);
61384f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal        if (sm == null) {
61484f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal            Log.e(TAG, "Cannot allocate SM for device " + device);
615aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta            return false;
616aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta        }
61784f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal
61884f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal        if (!sm.isConnected()) {
61984f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal            return false;
62084f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal        }
62184f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal        if (sm.isAudioOn()) {
622aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta            return false;
623aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta        }
62484f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal        sm.sendMessage(HeadsetClientStateMachine.CONNECT_AUDIO);
625aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta        return true;
626aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta    }
627aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta
62884f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal    boolean disconnectAudio(BluetoothDevice device) {
629aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta        enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, "Need BLUETOOTH_ADMIN permission");
63084f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal        HeadsetClientStateMachine sm = getStateMachine(device);
63184f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal        if (sm == null) {
63284f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal            Log.e(TAG, "Cannot allocate SM for device " + device);
63384f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal            return false;
63484f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal        }
63584f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal
63684f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal        if (!sm.isAudioOn()) {
637aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta            return false;
638aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta        }
63984f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal        sm.sendMessage(HeadsetClientStateMachine.DISCONNECT_AUDIO);
640aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta        return true;
641aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta    }
642aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta
643aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta    boolean holdCall(BluetoothDevice device) {
644aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta        enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
64584f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal        HeadsetClientStateMachine sm = getStateMachine(device);
64684f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal        if (sm == null) {
64784f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal            Log.e(TAG, "Cannot allocate SM for device " + device);
64884f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal            return false;
64984f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal        }
65084f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal
65184f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal        int connectionState = sm.getConnectionState(device);
652aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta        if (connectionState != BluetoothProfile.STATE_CONNECTED &&
653aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta                connectionState != BluetoothProfile.STATE_CONNECTING) {
654aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta            return false;
655aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta        }
65684f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal        Message msg = sm.obtainMessage(HeadsetClientStateMachine.HOLD_CALL);
65784f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal        sm.sendMessage(msg);
658aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta        return true;
659aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta    }
660aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta
661aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta    boolean acceptCall(BluetoothDevice device, int flag) {
662aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta        enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
663739d0edc69630535258722a38a4d22d26d2ccf37Joseph Pirozzo        /* Phonecalls from a single device are supported, hang up any calls on the other phone */
664739d0edc69630535258722a38a4d22d26d2ccf37Joseph Pirozzo        synchronized (this) {
665739d0edc69630535258722a38a4d22d26d2ccf37Joseph Pirozzo            for (Map.Entry<BluetoothDevice, HeadsetClientStateMachine> entry :
666739d0edc69630535258722a38a4d22d26d2ccf37Joseph Pirozzo                    mStateMachineMap.entrySet()) {
66760487bf626ff69f4c603dcaba23d45318915f4bdJoseph Pirozzo                if (entry.getValue() == null || entry.getKey().equals(device)) {
668739d0edc69630535258722a38a4d22d26d2ccf37Joseph Pirozzo                    continue;
669739d0edc69630535258722a38a4d22d26d2ccf37Joseph Pirozzo                }
670739d0edc69630535258722a38a4d22d26d2ccf37Joseph Pirozzo                int connectionState = entry.getValue().getConnectionState(entry.getKey());
671739d0edc69630535258722a38a4d22d26d2ccf37Joseph Pirozzo                if (DBG) {
672739d0edc69630535258722a38a4d22d26d2ccf37Joseph Pirozzo                    Log.d(TAG, "Accepting a call on device " + device
673739d0edc69630535258722a38a4d22d26d2ccf37Joseph Pirozzo                                    + ". Possibly disconnecting on " + entry.getValue());
674739d0edc69630535258722a38a4d22d26d2ccf37Joseph Pirozzo                }
67560487bf626ff69f4c603dcaba23d45318915f4bdJoseph Pirozzo                if (connectionState == BluetoothProfile.STATE_CONNECTED) {
676739d0edc69630535258722a38a4d22d26d2ccf37Joseph Pirozzo                    entry.getValue()
677739d0edc69630535258722a38a4d22d26d2ccf37Joseph Pirozzo                            .obtainMessage(HeadsetClientStateMachine.TERMINATE_CALL)
678739d0edc69630535258722a38a4d22d26d2ccf37Joseph Pirozzo                            .sendToTarget();
67960487bf626ff69f4c603dcaba23d45318915f4bdJoseph Pirozzo                }
680739d0edc69630535258722a38a4d22d26d2ccf37Joseph Pirozzo            }
681739d0edc69630535258722a38a4d22d26d2ccf37Joseph Pirozzo        }
68284f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal        HeadsetClientStateMachine sm = getStateMachine(device);
68384f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal        if (sm == null) {
68484f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal            Log.e(TAG, "Cannot allocate SM for device " + device);
68584f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal            return false;
68684f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal        }
68784f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal
68884f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal        int connectionState = sm.getConnectionState(device);
689739d0edc69630535258722a38a4d22d26d2ccf37Joseph Pirozzo        if (connectionState != BluetoothProfile.STATE_CONNECTED) {
690aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta            return false;
691aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta        }
69284f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal        Message msg = sm.obtainMessage(HeadsetClientStateMachine.ACCEPT_CALL);
693aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta        msg.arg1 = flag;
69484f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal        sm.sendMessage(msg);
695aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta        return true;
696aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta    }
697aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta
698aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta    boolean rejectCall(BluetoothDevice device) {
699aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta        enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
70084f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal        HeadsetClientStateMachine sm = getStateMachine(device);
70184f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal        if (sm == null) {
70284f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal            Log.e(TAG, "Cannot allocate SM for device " + device);
70384f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal            return false;
70484f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal        }
70584f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal
70684f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal        int connectionState = sm.getConnectionState(device);
707aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta        if (connectionState != BluetoothProfile.STATE_CONNECTED &&
708aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta                connectionState != BluetoothProfile.STATE_CONNECTING) {
709aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta            return false;
710aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta        }
711aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta
71284f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal        Message msg = sm.obtainMessage(HeadsetClientStateMachine.REJECT_CALL);
71384f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal        sm.sendMessage(msg);
714aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta        return true;
715aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta    }
716aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta
717ca6e7c65b87c52f5e81b273d7ad979a81b52f991Sanket Agarwal    boolean terminateCall(BluetoothDevice device, UUID uuid) {
718aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta        enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
71984f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal        HeadsetClientStateMachine sm = getStateMachine(device);
72084f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal        if (sm == null) {
72184f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal            Log.e(TAG, "Cannot allocate SM for device " + device);
72284f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal            return false;
72384f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal        }
72484f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal
72584f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal        int connectionState = sm.getConnectionState(device);
726aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta        if (connectionState != BluetoothProfile.STATE_CONNECTED &&
727aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta                connectionState != BluetoothProfile.STATE_CONNECTING) {
728aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta            return false;
729aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta        }
730aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta
73184f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal        Message msg = sm.obtainMessage(HeadsetClientStateMachine.TERMINATE_CALL);
732ca6e7c65b87c52f5e81b273d7ad979a81b52f991Sanket Agarwal        msg.obj = uuid;
73384f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal        sm.sendMessage(msg);
734aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta        return true;
735aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta    }
736aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta
737aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta    boolean enterPrivateMode(BluetoothDevice device, int index) {
738aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta        enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
73984f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal        HeadsetClientStateMachine sm = getStateMachine(device);
74084f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal        if (sm == null) {
74184f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal            Log.e(TAG, "Cannot allocate SM for device " + device);
74284f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal            return false;
74384f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal        }
74484f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal
74584f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal        int connectionState = sm.getConnectionState(device);
746aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta        if (connectionState != BluetoothProfile.STATE_CONNECTED &&
747aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta                connectionState != BluetoothProfile.STATE_CONNECTING) {
748aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta            return false;
749aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta        }
750aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta
75184f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal        Message msg = sm.obtainMessage(HeadsetClientStateMachine.ENTER_PRIVATE_MODE);
752aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta        msg.arg1 = index;
75384f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal        sm.sendMessage(msg);
754aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta        return true;
755aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta    }
756aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta
757ca6e7c65b87c52f5e81b273d7ad979a81b52f991Sanket Agarwal    BluetoothHeadsetClientCall dial(BluetoothDevice device, String number) {
758aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta        enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
75984f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal        HeadsetClientStateMachine sm = getStateMachine(device);
76084f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal        if (sm == null) {
76184f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal            Log.e(TAG, "Cannot allocate SM for device " + device);
76284f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal            return null;
76384f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal        }
76484f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal
76584f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal        int connectionState = sm.getConnectionState(device);
766aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta        if (connectionState != BluetoothProfile.STATE_CONNECTED &&
767aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta                connectionState != BluetoothProfile.STATE_CONNECTING) {
768ca6e7c65b87c52f5e81b273d7ad979a81b52f991Sanket Agarwal            return null;
769aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta        }
770aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta
771ca6e7c65b87c52f5e81b273d7ad979a81b52f991Sanket Agarwal        BluetoothHeadsetClientCall call = new BluetoothHeadsetClientCall(
772ca6e7c65b87c52f5e81b273d7ad979a81b52f991Sanket Agarwal            device, HeadsetClientStateMachine.HF_ORIGINATED_CALL_ID,
773ca6e7c65b87c52f5e81b273d7ad979a81b52f991Sanket Agarwal            BluetoothHeadsetClientCall.CALL_STATE_DIALING, number, false  /* multiparty */,
774ca6e7c65b87c52f5e81b273d7ad979a81b52f991Sanket Agarwal            true  /* outgoing */);
77584f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal        Message msg = sm.obtainMessage(HeadsetClientStateMachine.DIAL_NUMBER);
776ca6e7c65b87c52f5e81b273d7ad979a81b52f991Sanket Agarwal        msg.obj = call;
77784f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal        sm.sendMessage(msg);
778ca6e7c65b87c52f5e81b273d7ad979a81b52f991Sanket Agarwal        return call;
779aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta    }
780aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta
781aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta    public boolean sendDTMF(BluetoothDevice device, byte code) {
782aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta        enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
78384f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal        HeadsetClientStateMachine sm = getStateMachine(device);
78484f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal        if (sm == null) {
78584f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal            Log.e(TAG, "Cannot allocate SM for device " + device);
78684f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal            return false;
78784f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal        }
78884f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal
78984f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal        int connectionState = sm.getConnectionState(device);
790aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta        if (connectionState != BluetoothProfile.STATE_CONNECTED &&
791aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta                connectionState != BluetoothProfile.STATE_CONNECTING) {
792aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta            return false;
793aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta        }
79484f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal        Message msg = sm.obtainMessage(HeadsetClientStateMachine.SEND_DTMF);
795aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta        msg.arg1 = code;
79684f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal        sm.sendMessage(msg);
797aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta        return true;
798aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta    }
799aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta
800aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta    public boolean getLastVoiceTagNumber(BluetoothDevice device) {
80184f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal        return false;
802aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta    }
803aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta
8048d536f3db19e8ea7426e98e470dc15d10ecbae87Mike Lockwood    public List<BluetoothHeadsetClientCall> getCurrentCalls(BluetoothDevice device) {
805aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta        enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
80684f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal        HeadsetClientStateMachine sm = getStateMachine(device);
80784f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal        if (sm == null) {
80884f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal            Log.e(TAG, "Cannot allocate SM for device " + device);
80984f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal            return null;
81084f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal        }
81184f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal
81284f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal        int connectionState = sm.getConnectionState(device);
813aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta        if (connectionState != BluetoothProfile.STATE_CONNECTED) {
814aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta            return null;
815aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta        }
81684f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal        return sm.getCurrentCalls();
817aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta    }
818aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta
819aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta    public boolean explicitCallTransfer(BluetoothDevice device) {
820aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta        enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
82184f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal        HeadsetClientStateMachine sm = getStateMachine(device);
82284f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal        if (sm == null) {
82384f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal            Log.e(TAG, "Cannot allocate SM for device " + device);
82484f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal            return false;
82584f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal        }
82684f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal
82784f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal        int connectionState = sm.getConnectionState(device);
828aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta        if (connectionState != BluetoothProfile.STATE_CONNECTED &&
829aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta                connectionState != BluetoothProfile.STATE_CONNECTING) {
830aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta            return false;
831aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta        }
83284f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal        Message msg = sm.obtainMessage(HeadsetClientStateMachine.EXPLICIT_CALL_TRANSFER);
83384f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal        sm.sendMessage(msg);
834aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta        return true;
835aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta    }
836aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta
837aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta    public Bundle getCurrentAgEvents(BluetoothDevice device) {
838aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta        enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
83984f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal        HeadsetClientStateMachine sm = getStateMachine(device);
84084f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal        if (sm == null) {
84184f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal            Log.e(TAG, "Cannot allocate SM for device " + device);
84284f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal            return null;
84384f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal        }
84484f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal
84584f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal        int connectionState = sm.getConnectionState(device);
846aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta        if (connectionState != BluetoothProfile.STATE_CONNECTED) {
847aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta            return null;
848aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta        }
84984f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal        return sm.getCurrentAgEvents();
850aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta    }
851aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta
852aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta    public Bundle getCurrentAgFeatures(BluetoothDevice device) {
853aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta        enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
85484f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal        HeadsetClientStateMachine sm = getStateMachine(device);
85584f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal        if (sm == null) {
85684f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal            Log.e(TAG, "Cannot allocate SM for device " + device);
85784f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal            return null;
85884f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal        }
85984f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal        int connectionState = sm.getConnectionState(device);
860aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta        if (connectionState != BluetoothProfile.STATE_CONNECTED) {
861aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta            return null;
862aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta        }
86384f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal        return sm.getCurrentAgFeatures();
86484f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal    }
86584f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal
86684f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal    // Handle messages from native (JNI) to java
86784f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal    public void messageFromNative(StackEvent stackEvent) {
86884f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal        HeadsetClientStateMachine sm = getStateMachine(stackEvent.device);
86984f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal        if (sm == null) {
87084f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal            Log.w(TAG, "No SM found for event " + stackEvent);
87184f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal        }
87284f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal
87384f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal        sm.sendMessage(StackEvent.STACK_EVENT, stackEvent);
87484f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal    }
87584f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal
87684f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal    // State machine management
87784f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal    private synchronized HeadsetClientStateMachine getStateMachine(BluetoothDevice device) {
87884f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal        if (device == null) {
87984f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal            Log.e(TAG, "getStateMachine failed: Device cannot be null");
88084f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal            return null;
88184f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal        }
88284f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal
88384f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal        HeadsetClientStateMachine sm = mStateMachineMap.get(device);
88484f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal        if (sm != null) {
88584f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal            if (DBG) {
88684f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal                Log.d(TAG, "Found SM for device " + device);
88784f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal            }
88884f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal            return sm;
88984f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal        }
89084f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal
89184f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal        // There is a possibility of a DOS attack if someone populates here with a lot of fake
89284f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal        // BluetoothAddresses. If it so happens instead of blowing up we can atleast put a limit on
89384f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal        // how long the attack would survive
89484f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal        if (mStateMachineMap.keySet().size() > MAX_STATE_MACHINES_POSSIBLE) {
89584f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal            Log.e(TAG, "Max state machines reached, possible DOS attack " +
89684f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal                MAX_STATE_MACHINES_POSSIBLE);
89784f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal            return null;
89884f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal        }
89984f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal
90084f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal        // Allocate a new SM
90184f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal        Log.d(TAG, "Creating a new state machine");
90284f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal        sm = mSmFactory.make(this, mSmThread);
90384f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal        mStateMachineMap.put(device, sm);
90484f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal        return sm;
905aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta    }
906838949d46a4cc054985a8cfd682004f8dd6d3bbbMike Lockwood
907739d0edc69630535258722a38a4d22d26d2ccf37Joseph Pirozzo    // Check if any of the state machines have routed the SCO audio stream.
908739d0edc69630535258722a38a4d22d26d2ccf37Joseph Pirozzo    synchronized boolean isScoRouted() {
909739d0edc69630535258722a38a4d22d26d2ccf37Joseph Pirozzo        for (Map.Entry<BluetoothDevice, HeadsetClientStateMachine> entry :
910739d0edc69630535258722a38a4d22d26d2ccf37Joseph Pirozzo                mStateMachineMap.entrySet()) {
911739d0edc69630535258722a38a4d22d26d2ccf37Joseph Pirozzo            if (entry.getValue() != null) {
912739d0edc69630535258722a38a4d22d26d2ccf37Joseph Pirozzo                int audioState = entry.getValue().getAudioState(entry.getKey());
913739d0edc69630535258722a38a4d22d26d2ccf37Joseph Pirozzo                if (audioState == BluetoothHeadsetClient.STATE_AUDIO_CONNECTED) {
914739d0edc69630535258722a38a4d22d26d2ccf37Joseph Pirozzo                    if (DBG) {
915739d0edc69630535258722a38a4d22d26d2ccf37Joseph Pirozzo                        Log.d(TAG, "Device " + entry.getKey() + " audio state " + audioState
916739d0edc69630535258722a38a4d22d26d2ccf37Joseph Pirozzo                                        + " Connected");
917739d0edc69630535258722a38a4d22d26d2ccf37Joseph Pirozzo                    }
918739d0edc69630535258722a38a4d22d26d2ccf37Joseph Pirozzo                    return true;
919739d0edc69630535258722a38a4d22d26d2ccf37Joseph Pirozzo                }
9202d9c62894634e9c4aa947ddf2cc50f4482163cf1Sanket Agarwal            }
9212d9c62894634e9c4aa947ddf2cc50f4482163cf1Sanket Agarwal        }
922739d0edc69630535258722a38a4d22d26d2ccf37Joseph Pirozzo        return false;
9232d9c62894634e9c4aa947ddf2cc50f4482163cf1Sanket Agarwal    }
9242d9c62894634e9c4aa947ddf2cc50f4482163cf1Sanket Agarwal
925838949d46a4cc054985a8cfd682004f8dd6d3bbbMike Lockwood    @Override
92684f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal    public synchronized void dump(StringBuilder sb) {
927838949d46a4cc054985a8cfd682004f8dd6d3bbbMike Lockwood        super.dump(sb);
92884f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal        for (HeadsetClientStateMachine sm : mStateMachineMap.values()) {
92984f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal            if (sm != null) {
93084f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal                println(sb, "State machine:");
93184f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal                println(sb, "=============");
93284f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal                sm.dump(sb);
93384f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal            }
934838949d46a4cc054985a8cfd682004f8dd6d3bbbMike Lockwood        }
935838949d46a4cc054985a8cfd682004f8dd6d3bbbMike Lockwood    }
93684f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal
93784f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal    // For testing
93884f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal    protected synchronized Map<BluetoothDevice, HeadsetClientStateMachine> getStateMachineMap() {
93984f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal        return mStateMachineMap;
94084f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal    }
94184f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal
94284f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal    protected void setSMFactory(HeadsetClientStateMachineFactory factory) {
94384f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal        mSmFactory = factory;
94484f977c60b8d7feea9c9b20d60a7eb8e3e4869a9Sanket Agarwal    }
945aebc726105204f8a7b977eb3556c14b5ba18a5caHemant Gupta}
946