127a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon/*
227a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon * Copyright (C) 2013 The Android Open Source Project
327a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon *
427a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon * Licensed under the Apache License, Version 2.0 (the "License");
527a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon * you may not use this file except in compliance with the License.
627a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon * You may obtain a copy of the License at
727a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon *
827a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon *      http://www.apache.org/licenses/LICENSE-2.0
927a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon *
1027a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon * Unless required by applicable law or agreed to in writing, software
1127a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon * distributed under the License is distributed on an "AS IS" BASIS,
1227a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1327a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon * See the License for the specific language governing permissions and
1427a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon * limitations under the License.
1527a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon */
1627a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon
1727a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordonpackage com.android.phone;
1827a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon
1927a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordonimport com.google.android.collect.Lists;
2027a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordonimport com.google.common.base.Preconditions;
2127a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon
2227a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordonimport android.bluetooth.BluetoothAdapter;
2327a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordonimport android.bluetooth.BluetoothDevice;
2427a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordonimport android.bluetooth.BluetoothHeadset;
2527a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordonimport android.bluetooth.BluetoothProfile;
2627a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordonimport android.content.BroadcastReceiver;
2727a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordonimport android.content.Context;
2827a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordonimport android.content.Intent;
2927a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordonimport android.content.IntentFilter;
3027a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordonimport android.os.SystemClock;
3127a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordonimport android.os.SystemProperties;
3227a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordonimport android.util.Log;
3327a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon
3427a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordonimport com.android.internal.telephony.CallManager;
35de41f67985e48d9cc17d48a9299648966b9bc7e5Yorke Leeimport com.android.internal.telephony.Connection;
362c2d3cf8cca6bb471fbd26770b1736af2a32d619Santos Cordonimport com.android.services.telephony.common.Call;
3727a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon
3827a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordonimport java.util.List;
3927a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon
4027a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon/**
41593ab38970a84a60ac39edba4306647c8b66436dSantos Cordon * Listens to and caches bluetooth headset state.  Used By the AudioRouter for maintaining
42593ab38970a84a60ac39edba4306647c8b66436dSantos Cordon * overall audio state for use in the UI layer. Also provides method for connecting the bluetooth
43593ab38970a84a60ac39edba4306647c8b66436dSantos Cordon * headset to the phone call.
4427a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon */
452c2d3cf8cca6bb471fbd26770b1736af2a32d619Santos Cordonpublic class BluetoothManager implements CallModeler.Listener {
46593ab38970a84a60ac39edba4306647c8b66436dSantos Cordon    private static final String LOG_TAG = BluetoothManager.class.getSimpleName();
4727a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon    private static final boolean DBG =
4827a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon            (PhoneGlobals.DBG_LEVEL >= 1) && (SystemProperties.getInt("ro.debuggable", 0) == 1);
4927a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon    private static final boolean VDBG = (PhoneGlobals.DBG_LEVEL >= 2);
5027a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon
5127a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon    private final BluetoothAdapter mBluetoothAdapter;
5227a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon    private final CallManager mCallManager;
5327a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon    private final Context mContext;
542c2d3cf8cca6bb471fbd26770b1736af2a32d619Santos Cordon    private final CallModeler mCallModeler;
5527a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon
5627a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon    private BluetoothHeadset mBluetoothHeadset;
5727a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon    private int mBluetoothHeadsetState = BluetoothProfile.STATE_DISCONNECTED;
5827a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon    private int mBluetoothHeadsetAudioState = BluetoothHeadset.STATE_AUDIO_DISCONNECTED;
5927a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon    private boolean mShowBluetoothIndication = false;
6027a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon    private boolean mBluetoothConnectionPending = false;
6127a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon    private long mBluetoothConnectionRequestTime;
6227a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon
6327a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon    // Broadcast receiver for various intent broadcasts (see onCreate())
6427a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon    private final BroadcastReceiver mReceiver = new BluetoothBroadcastReceiver();
6527a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon
6627a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon    private final List<BluetoothIndicatorListener> mListeners = Lists.newArrayList();
6727a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon
682c2d3cf8cca6bb471fbd26770b1736af2a32d619Santos Cordon    public BluetoothManager(Context context, CallManager callManager, CallModeler callModeler) {
6927a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon        mContext = context;
7027a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon        mCallManager = callManager;
712c2d3cf8cca6bb471fbd26770b1736af2a32d619Santos Cordon        mCallModeler = callModeler;
7227a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon        mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
7327a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon
7427a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon        init(mContext);
7527a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon    }
7627a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon
7727a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon    /* package */ boolean isBluetoothHeadsetAudioOn() {
7827a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon        return (mBluetoothHeadsetAudioState != BluetoothHeadset.STATE_AUDIO_DISCONNECTED);
7927a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon    }
8027a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon
8127a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon    //
8227a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon    // Bluetooth helper methods.
8327a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon    //
8427a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon    // - BluetoothAdapter is the Bluetooth system service.  If
8527a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon    //   getDefaultAdapter() returns null
8627a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon    //   then the device is not BT capable.  Use BluetoothDevice.isEnabled()
8727a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon    //   to see if BT is enabled on the device.
8827a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon    //
8927a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon    // - BluetoothHeadset is the API for the control connection to a
9027a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon    //   Bluetooth Headset.  This lets you completely connect/disconnect a
9127a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon    //   headset (which we don't do from the Phone UI!) but also lets you
9227a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon    //   get the address of the currently active headset and see whether
9327a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon    //   it's currently connected.
9427a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon
9527a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon    /**
9627a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon     * @return true if the Bluetooth on/off switch in the UI should be
9727a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon     *         available to the user (i.e. if the device is BT-capable
9827a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon     *         and a headset is connected.)
9927a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon     */
10027a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon    /* package */ boolean isBluetoothAvailable() {
10127a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon        if (VDBG) log("isBluetoothAvailable()...");
10227a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon
10327a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon        // There's no need to ask the Bluetooth system service if BT is enabled:
10427a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon        //
10527a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon        //    BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
10627a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon        //    if ((adapter == null) || !adapter.isEnabled()) {
10727a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon        //        if (DBG) log("  ==> FALSE (BT not enabled)");
10827a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon        //        return false;
10927a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon        //    }
11027a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon        //    if (DBG) log("  - BT enabled!  device name " + adapter.getName()
11127a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon        //                 + ", address " + adapter.getAddress());
11227a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon        //
11327a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon        // ...since we already have a BluetoothHeadset instance.  We can just
11427a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon        // call isConnected() on that, and assume it'll be false if BT isn't
11527a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon        // enabled at all.
11627a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon
11727a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon        // Check if there's a connected headset, using the BluetoothHeadset API.
11827a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon        boolean isConnected = false;
11927a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon        if (mBluetoothHeadset != null) {
12027a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon            List<BluetoothDevice> deviceList = mBluetoothHeadset.getConnectedDevices();
12127a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon
12227a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon            if (deviceList.size() > 0) {
12327a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon                BluetoothDevice device = deviceList.get(0);
12427a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon                isConnected = true;
12527a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon
12627a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon                if (VDBG) log("  - headset state = " +
12727a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon                              mBluetoothHeadset.getConnectionState(device));
12827a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon                if (VDBG) log("  - headset address: " + device);
12927a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon                if (VDBG) log("  - isConnected: " + isConnected);
13027a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon            }
13127a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon        }
13227a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon
13327a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon        if (VDBG) log("  ==> " + isConnected);
13427a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon        return isConnected;
13527a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon    }
13627a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon
13727a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon    /**
13827a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon     * @return true if a BT Headset is available, and its audio is currently connected.
13927a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon     */
14027a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon    /* package */ boolean isBluetoothAudioConnected() {
14127a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon        if (mBluetoothHeadset == null) {
14227a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon            if (VDBG) log("isBluetoothAudioConnected: ==> FALSE (null mBluetoothHeadset)");
14327a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon            return false;
14427a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon        }
14527a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon        List<BluetoothDevice> deviceList = mBluetoothHeadset.getConnectedDevices();
14627a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon
14727a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon        if (deviceList.isEmpty()) {
14827a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon            return false;
14927a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon        }
15027a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon        BluetoothDevice device = deviceList.get(0);
15127a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon        boolean isAudioOn = mBluetoothHeadset.isAudioConnected(device);
15227a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon        if (VDBG) log("isBluetoothAudioConnected: ==> isAudioOn = " + isAudioOn);
15327a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon        return isAudioOn;
15427a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon    }
15527a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon
15627a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon    /**
15727a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon     * Helper method used to control the onscreen "Bluetooth" indication;
15827a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon     * see InCallControlState.bluetoothIndicatorOn.
15927a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon     *
16027a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon     * @return true if a BT device is available and its audio is currently connected,
16127a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon     *              <b>or</b> if we issued a BluetoothHeadset.connectAudio()
16227a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon     *              call within the last 5 seconds (which presumably means
16327a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon     *              that the BT audio connection is currently being set
16427a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon     *              up, and will be connected soon.)
16527a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon     */
16627a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon    /* package */ boolean isBluetoothAudioConnectedOrPending() {
16727a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon        if (isBluetoothAudioConnected()) {
16827a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon            if (VDBG) log("isBluetoothAudioConnectedOrPending: ==> TRUE (really connected)");
16927a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon            return true;
17027a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon        }
17127a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon
17227a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon        // If we issued a connectAudio() call "recently enough", even
17327a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon        // if BT isn't actually connected yet, let's still pretend BT is
17427a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon        // on.  This makes the onscreen indication more responsive.
17527a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon        if (mBluetoothConnectionPending) {
17627a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon            long timeSinceRequest =
17727a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon                    SystemClock.elapsedRealtime() - mBluetoothConnectionRequestTime;
17827a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon            if (timeSinceRequest < 5000 /* 5 seconds */) {
17927a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon                if (VDBG) log("isBluetoothAudioConnectedOrPending: ==> TRUE (requested "
18027a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon                             + timeSinceRequest + " msec ago)");
18127a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon                return true;
18227a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon            } else {
18327a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon                if (VDBG) log("isBluetoothAudioConnectedOrPending: ==> FALSE (request too old: "
18427a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon                             + timeSinceRequest + " msec ago)");
18527a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon                mBluetoothConnectionPending = false;
18627a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon                return false;
18727a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon            }
18827a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon        }
18927a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon
19027a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon        if (VDBG) log("isBluetoothAudioConnectedOrPending: ==> FALSE");
19127a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon        return false;
19227a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon    }
19327a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon
19427a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon    /**
19527a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon     * @return true if the onscreen UI should currently be showing the
19627a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon     * special "bluetooth is active" indication in a couple of places (in
19727a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon     * which UI elements turn blue and/or show the bluetooth logo.)
19827a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon     *
19927a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon     * This depends on the BluetoothHeadset state *and* the current
20027a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon     * telephony state; see shouldShowBluetoothIndication().
20127a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon     *
20227a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon     * @see CallCard
20327a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon     * @see NotificationMgr.updateInCallNotification
20427a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon     */
20527a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon    /* package */ boolean showBluetoothIndication() {
20627a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon        return mShowBluetoothIndication;
20727a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon    }
20827a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon
20927a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon    /**
21027a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon     * Recomputes the mShowBluetoothIndication flag based on the current
21127a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon     * bluetooth state and current telephony state.
21227a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon     *
21327a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon     * This needs to be called any time the bluetooth headset state or the
21427a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon     * telephony state changes.
21527a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon     */
21627a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon    /* package */ void updateBluetoothIndication() {
21727a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon        mShowBluetoothIndication = shouldShowBluetoothIndication(mBluetoothHeadsetState,
21827a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon                                                                 mBluetoothHeadsetAudioState,
21927a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon                                                                 mCallManager);
22027a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon
22127a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon        notifyListeners(mShowBluetoothIndication);
22227a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon    }
22327a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon
2249b7bac7705b3afcae7b010edc2032c7c0f37f770Santos Cordon    public void addBluetoothIndicatorListener(BluetoothIndicatorListener listener) {
22527a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon        if (!mListeners.contains(listener)) {
22627a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon            mListeners.add(listener);
22727a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon        }
22827a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon    }
22927a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon
2309b7bac7705b3afcae7b010edc2032c7c0f37f770Santos Cordon    public void removeBluetoothIndicatorListener(BluetoothIndicatorListener listener) {
2319b7bac7705b3afcae7b010edc2032c7c0f37f770Santos Cordon        if (mListeners.contains(listener)) {
2329b7bac7705b3afcae7b010edc2032c7c0f37f770Santos Cordon            mListeners.remove(listener);
2339b7bac7705b3afcae7b010edc2032c7c0f37f770Santos Cordon        }
2349b7bac7705b3afcae7b010edc2032c7c0f37f770Santos Cordon    }
2359b7bac7705b3afcae7b010edc2032c7c0f37f770Santos Cordon
23627a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon    private void notifyListeners(boolean showBluetoothOn) {
23727a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon        for (int i = 0; i < mListeners.size(); i++) {
23827a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon            mListeners.get(i).onBluetoothIndicationChange(showBluetoothOn, this);
23927a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon        }
24027a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon    }
24127a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon
24227a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon    private void init(Context context) {
24327a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon        Preconditions.checkNotNull(context);
24427a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon
24527a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon        if (mBluetoothAdapter != null) {
24627a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon            mBluetoothAdapter.getProfileProxy(context, mBluetoothProfileServiceListener,
24727a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon                                    BluetoothProfile.HEADSET);
24827a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon        }
24927a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon
25027a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon        // Register for misc other intent broadcasts.
25127a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon        IntentFilter intentFilter =
25227a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon                new IntentFilter(BluetoothHeadset.ACTION_CONNECTION_STATE_CHANGED);
25327a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon        intentFilter.addAction(BluetoothHeadset.ACTION_AUDIO_STATE_CHANGED);
25427a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon        context.registerReceiver(mReceiver, intentFilter);
2552c2d3cf8cca6bb471fbd26770b1736af2a32d619Santos Cordon
2562c2d3cf8cca6bb471fbd26770b1736af2a32d619Santos Cordon        mCallModeler.addListener(this);
25727a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon    }
25827a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon
25927a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon    private void tearDown() {
26027a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon        if (mBluetoothHeadset != null) {
26127a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon            mBluetoothAdapter.closeProfileProxy(BluetoothProfile.HEADSET, mBluetoothHeadset);
26227a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon            mBluetoothHeadset = null;
26327a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon        }
26427a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon    }
26527a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon
26627a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon    private BluetoothProfile.ServiceListener mBluetoothProfileServiceListener =
26727a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon             new BluetoothProfile.ServiceListener() {
26827a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon         @Override
26927a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon         public void onServiceConnected(int profile, BluetoothProfile proxy) {
27027a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon             mBluetoothHeadset = (BluetoothHeadset) proxy;
27127a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon             if (VDBG) log("- Got BluetoothHeadset: " + mBluetoothHeadset);
27227a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon         }
27327a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon
27427a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon         @Override
27527a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon         public void onServiceDisconnected(int profile) {
27627a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon             mBluetoothHeadset = null;
27727a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon         }
27827a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon    };
27927a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon
28027a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon    /**
28127a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon     * UI policy helper function for the couple of places in the UI that
28227a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon     * have some way of indicating that "bluetooth is in use."
28327a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon     *
28427a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon     * @return true if the onscreen UI should indicate that "bluetooth is in use",
28527a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon     *         based on the specified bluetooth headset state, and the
28627a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon     *         current state of the phone.
28727a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon     * @see showBluetoothIndication()
28827a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon     */
28927a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon    private static boolean shouldShowBluetoothIndication(int bluetoothState,
29027a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon                                                         int bluetoothAudioState,
29127a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon                                                         CallManager cm) {
29227a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon        // We want the UI to indicate that "bluetooth is in use" in two
29327a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon        // slightly different cases:
29427a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon        //
29527a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon        // (a) The obvious case: if a bluetooth headset is currently in
29627a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon        //     use for an ongoing call.
29727a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon        //
29827a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon        // (b) The not-so-obvious case: if an incoming call is ringing,
29927a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon        //     and we expect that audio *will* be routed to a bluetooth
30027a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon        //     headset once the call is answered.
30127a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon
30227a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon        switch (cm.getState()) {
30327a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon            case OFFHOOK:
30427a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon                // This covers normal active calls, and also the case if
30527a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon                // the foreground call is DIALING or ALERTING.  In this
30627a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon                // case, bluetooth is considered "active" if a headset
30727a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon                // is connected *and* audio is being routed to it.
30827a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon                return ((bluetoothState == BluetoothHeadset.STATE_CONNECTED)
30927a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon                        && (bluetoothAudioState == BluetoothHeadset.STATE_AUDIO_CONNECTED));
31027a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon
31127a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon            case RINGING:
31227a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon                // If an incoming call is ringing, we're *not* yet routing
31327a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon                // audio to the headset (since there's no in-call audio
31427a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon                // yet!)  In this case, if a bluetooth headset is
31527a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon                // connected at all, we assume that it'll become active
31627a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon                // once the user answers the phone.
31727a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon                return (bluetoothState == BluetoothHeadset.STATE_CONNECTED);
31827a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon
31927a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon            default:  // Presumably IDLE
32027a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon                return false;
32127a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon        }
32227a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon    }
32327a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon
32427a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon    private void dumpBluetoothState() {
32527a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon        log("============== dumpBluetoothState() =============");
32627a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon        log("= isBluetoothAvailable: " + isBluetoothAvailable());
32727a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon        log("= isBluetoothAudioConnected: " + isBluetoothAudioConnected());
32827a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon        log("= isBluetoothAudioConnectedOrPending: " + isBluetoothAudioConnectedOrPending());
32927a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon        log("= PhoneApp.showBluetoothIndication: "
33027a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon            + showBluetoothIndication());
33127a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon        log("=");
33227a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon        if (mBluetoothAdapter != null) {
33327a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon            if (mBluetoothHeadset != null) {
33427a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon                List<BluetoothDevice> deviceList = mBluetoothHeadset.getConnectedDevices();
33527a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon
33627a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon                if (deviceList.size() > 0) {
33727a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon                    BluetoothDevice device = deviceList.get(0);
33827a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon                    log("= BluetoothHeadset.getCurrentDevice: " + device);
33927a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon                    log("= BluetoothHeadset.State: "
34027a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon                        + mBluetoothHeadset.getConnectionState(device));
34127a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon                    log("= BluetoothHeadset audio connected: " +
34227a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon                        mBluetoothHeadset.isAudioConnected(device));
34327a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon                }
34427a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon            } else {
34527a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon                log("= mBluetoothHeadset is null");
34627a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon            }
34727a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon        } else {
34827a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon            log("= mBluetoothAdapter is null; device is not BT capable");
34927a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon        }
35027a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon    }
35127a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon
35227a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon    /* package */ void connectBluetoothAudio() {
35327a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon        if (VDBG) log("connectBluetoothAudio()...");
35427a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon        if (mBluetoothHeadset != null) {
35527a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon            // TODO(BT) check return
35627a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon            mBluetoothHeadset.connectAudio();
35727a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon        }
35827a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon
35927a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon        // Watch out: The bluetooth connection doesn't happen instantly;
36027a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon        // the connectAudio() call returns instantly but does its real
36127a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon        // work in another thread.  The mBluetoothConnectionPending flag
36227a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon        // is just a little trickery to ensure that the onscreen UI updates
36327a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon        // instantly. (See isBluetoothAudioConnectedOrPending() above.)
36427a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon        mBluetoothConnectionPending = true;
36527a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon        mBluetoothConnectionRequestTime = SystemClock.elapsedRealtime();
36627a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon    }
36727a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon
36827a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon    /* package */ void disconnectBluetoothAudio() {
36927a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon        if (VDBG) log("disconnectBluetoothAudio()...");
37027a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon        if (mBluetoothHeadset != null) {
37127a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon            mBluetoothHeadset.disconnectAudio();
37227a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon        }
37327a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon        mBluetoothConnectionPending = false;
37427a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon    }
37527a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon
37627a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon    /**
37727a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon     * Receiver for misc intent broadcasts the BluetoothManager cares about.
37827a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon     */
37927a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon    private class BluetoothBroadcastReceiver extends BroadcastReceiver {
38027a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon        @Override
38127a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon        public void onReceive(Context context, Intent intent) {
38227a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon            String action = intent.getAction();
38327a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon
38427a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon            if (action.equals(BluetoothHeadset.ACTION_CONNECTION_STATE_CHANGED)) {
38527a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon                mBluetoothHeadsetState = intent.getIntExtra(BluetoothHeadset.EXTRA_STATE,
38627a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon                                                          BluetoothHeadset.STATE_DISCONNECTED);
38727a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon                if (VDBG) Log.d(LOG_TAG, "mReceiver: HEADSET_STATE_CHANGED_ACTION");
38827a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon                if (VDBG) Log.d(LOG_TAG, "==> new state: " + mBluetoothHeadsetState);
38927a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon                // Also update any visible UI if necessary
39027a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon                updateBluetoothIndication();
39127a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon            } else if (action.equals(BluetoothHeadset.ACTION_AUDIO_STATE_CHANGED)) {
39227a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon                mBluetoothHeadsetAudioState =
39327a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon                        intent.getIntExtra(BluetoothHeadset.EXTRA_STATE,
39427a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon                                           BluetoothHeadset.STATE_AUDIO_DISCONNECTED);
39527a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon                if (VDBG) Log.d(LOG_TAG, "mReceiver: HEADSET_AUDIO_STATE_CHANGED_ACTION");
39627a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon                if (VDBG) Log.d(LOG_TAG, "==> new state: " + mBluetoothHeadsetAudioState);
39727a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon                updateBluetoothIndication();
39827a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon            }
39927a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon        }
40027a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon    }
40127a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon
4022c2d3cf8cca6bb471fbd26770b1736af2a32d619Santos Cordon    @Override
4032c2d3cf8cca6bb471fbd26770b1736af2a32d619Santos Cordon    public void onDisconnect(Call call) {
4042c2d3cf8cca6bb471fbd26770b1736af2a32d619Santos Cordon        updateBluetoothIndication();
4052c2d3cf8cca6bb471fbd26770b1736af2a32d619Santos Cordon    }
4062c2d3cf8cca6bb471fbd26770b1736af2a32d619Santos Cordon
4072c2d3cf8cca6bb471fbd26770b1736af2a32d619Santos Cordon    @Override
4086c6b27265806a053193b8ccbc57f66b1feb8e5abChiao Cheng    public void onIncoming(Call call) {
4092c2d3cf8cca6bb471fbd26770b1736af2a32d619Santos Cordon        // An incoming call can affect bluetooth indicator, so we update it whenever there is
4102c2d3cf8cca6bb471fbd26770b1736af2a32d619Santos Cordon        // a change to any of the calls.
4112c2d3cf8cca6bb471fbd26770b1736af2a32d619Santos Cordon        updateBluetoothIndication();
4122c2d3cf8cca6bb471fbd26770b1736af2a32d619Santos Cordon    }
4132c2d3cf8cca6bb471fbd26770b1736af2a32d619Santos Cordon
4142c2d3cf8cca6bb471fbd26770b1736af2a32d619Santos Cordon    @Override
4156c6b27265806a053193b8ccbc57f66b1feb8e5abChiao Cheng    public void onUpdate(List<Call> calls) {
4162c2d3cf8cca6bb471fbd26770b1736af2a32d619Santos Cordon        updateBluetoothIndication();
4172c2d3cf8cca6bb471fbd26770b1736af2a32d619Santos Cordon    }
4182c2d3cf8cca6bb471fbd26770b1736af2a32d619Santos Cordon
4193f015c9e837c4e7a2b710d32f16e81eb684dda74Chiao Cheng    @Override
420de41f67985e48d9cc17d48a9299648966b9bc7e5Yorke Lee    public void onPostDialAction(Connection.PostDialState state, int callId, String chars, char c) {
4213f015c9e837c4e7a2b710d32f16e81eb684dda74Chiao Cheng        // no-op
4223f015c9e837c4e7a2b710d32f16e81eb684dda74Chiao Cheng    }
4233f015c9e837c4e7a2b710d32f16e81eb684dda74Chiao Cheng
42427a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon    private void log(String msg) {
42527a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon        Log.d(LOG_TAG, msg);
42627a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon    }
42727a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon
42827a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon    /* package */ interface BluetoothIndicatorListener {
4299b7bac7705b3afcae7b010edc2032c7c0f37f770Santos Cordon        public void onBluetoothIndicationChange(boolean isConnected, BluetoothManager manager);
43027a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon    }
43127a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon}
432