BluetoothManager.java revision 593ab38970a84a60ac39edba4306647c8b66436d
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.Handler; 3127a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordonimport android.os.Message; 3227a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordonimport android.os.SystemClock; 3327a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordonimport android.os.SystemProperties; 3427a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordonimport android.util.Log; 3527a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon 3627a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordonimport com.android.internal.telephony.CallManager; 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 */ 4527a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordonpublic class BluetoothManager { 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; 5427a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon 5527a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon private BluetoothHeadset mBluetoothHeadset; 5627a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon private int mBluetoothHeadsetState = BluetoothProfile.STATE_DISCONNECTED; 5727a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon private int mBluetoothHeadsetAudioState = BluetoothHeadset.STATE_AUDIO_DISCONNECTED; 5827a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon private boolean mShowBluetoothIndication = false; 5927a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon private boolean mBluetoothConnectionPending = false; 6027a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon private long mBluetoothConnectionRequestTime; 6127a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon 6227a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon // Broadcast receiver for various intent broadcasts (see onCreate()) 6327a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon private final BroadcastReceiver mReceiver = new BluetoothBroadcastReceiver(); 6427a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon 6527a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon private final List<BluetoothIndicatorListener> mListeners = Lists.newArrayList(); 6627a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon 6727a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon public BluetoothManager(Context context, CallManager callManager) { 6827a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon mContext = context; 6927a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon mCallManager = callManager; 7027a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter(); 7127a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon 7227a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon init(mContext); 7327a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon // TODO(klp): Listen for changes to the call list/state. 7427a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon } 7527a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon 7627a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon /* package */ boolean isBluetoothHeadsetAudioOn() { 7727a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon return (mBluetoothHeadsetAudioState != BluetoothHeadset.STATE_AUDIO_DISCONNECTED); 7827a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon } 7927a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon 8027a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon // 8127a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon // Bluetooth helper methods. 8227a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon // 8327a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon // - BluetoothAdapter is the Bluetooth system service. If 8427a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon // getDefaultAdapter() returns null 8527a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon // then the device is not BT capable. Use BluetoothDevice.isEnabled() 8627a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon // to see if BT is enabled on the device. 8727a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon // 8827a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon // - BluetoothHeadset is the API for the control connection to a 8927a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon // Bluetooth Headset. This lets you completely connect/disconnect a 9027a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon // headset (which we don't do from the Phone UI!) but also lets you 9127a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon // get the address of the currently active headset and see whether 9227a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon // it's currently connected. 9327a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon 9427a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon /** 9527a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon * @return true if the Bluetooth on/off switch in the UI should be 9627a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon * available to the user (i.e. if the device is BT-capable 9727a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon * and a headset is connected.) 9827a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon */ 9927a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon /* package */ boolean isBluetoothAvailable() { 10027a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon if (VDBG) log("isBluetoothAvailable()..."); 10127a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon 10227a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon // There's no need to ask the Bluetooth system service if BT is enabled: 10327a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon // 10427a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon // BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter(); 10527a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon // if ((adapter == null) || !adapter.isEnabled()) { 10627a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon // if (DBG) log(" ==> FALSE (BT not enabled)"); 10727a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon // return false; 10827a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon // } 10927a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon // if (DBG) log(" - BT enabled! device name " + adapter.getName() 11027a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon // + ", address " + adapter.getAddress()); 11127a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon // 11227a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon // ...since we already have a BluetoothHeadset instance. We can just 11327a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon // call isConnected() on that, and assume it'll be false if BT isn't 11427a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon // enabled at all. 11527a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon 11627a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon // Check if there's a connected headset, using the BluetoothHeadset API. 11727a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon boolean isConnected = false; 11827a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon if (mBluetoothHeadset != null) { 11927a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon List<BluetoothDevice> deviceList = mBluetoothHeadset.getConnectedDevices(); 12027a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon 12127a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon if (deviceList.size() > 0) { 12227a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon BluetoothDevice device = deviceList.get(0); 12327a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon isConnected = true; 12427a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon 12527a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon if (VDBG) log(" - headset state = " + 12627a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon mBluetoothHeadset.getConnectionState(device)); 12727a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon if (VDBG) log(" - headset address: " + device); 12827a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon if (VDBG) log(" - isConnected: " + isConnected); 12927a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon } 13027a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon } 13127a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon 13227a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon if (VDBG) log(" ==> " + isConnected); 13327a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon return isConnected; 13427a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon } 13527a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon 13627a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon /** 13727a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon * @return true if a BT Headset is available, and its audio is currently connected. 13827a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon */ 13927a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon /* package */ boolean isBluetoothAudioConnected() { 14027a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon if (mBluetoothHeadset == null) { 14127a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon if (VDBG) log("isBluetoothAudioConnected: ==> FALSE (null mBluetoothHeadset)"); 14227a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon return false; 14327a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon } 14427a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon List<BluetoothDevice> deviceList = mBluetoothHeadset.getConnectedDevices(); 14527a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon 14627a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon if (deviceList.isEmpty()) { 14727a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon return false; 14827a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon } 14927a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon BluetoothDevice device = deviceList.get(0); 15027a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon boolean isAudioOn = mBluetoothHeadset.isAudioConnected(device); 15127a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon if (VDBG) log("isBluetoothAudioConnected: ==> isAudioOn = " + isAudioOn); 15227a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon return isAudioOn; 15327a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon } 15427a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon 15527a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon /** 15627a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon * Helper method used to control the onscreen "Bluetooth" indication; 15727a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon * see InCallControlState.bluetoothIndicatorOn. 15827a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon * 15927a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon * @return true if a BT device is available and its audio is currently connected, 16027a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon * <b>or</b> if we issued a BluetoothHeadset.connectAudio() 16127a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon * call within the last 5 seconds (which presumably means 16227a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon * that the BT audio connection is currently being set 16327a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon * up, and will be connected soon.) 16427a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon */ 16527a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon /* package */ boolean isBluetoothAudioConnectedOrPending() { 16627a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon if (isBluetoothAudioConnected()) { 16727a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon if (VDBG) log("isBluetoothAudioConnectedOrPending: ==> TRUE (really connected)"); 16827a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon return true; 16927a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon } 17027a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon 17127a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon // If we issued a connectAudio() call "recently enough", even 17227a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon // if BT isn't actually connected yet, let's still pretend BT is 17327a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon // on. This makes the onscreen indication more responsive. 17427a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon if (mBluetoothConnectionPending) { 17527a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon long timeSinceRequest = 17627a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon SystemClock.elapsedRealtime() - mBluetoothConnectionRequestTime; 17727a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon if (timeSinceRequest < 5000 /* 5 seconds */) { 17827a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon if (VDBG) log("isBluetoothAudioConnectedOrPending: ==> TRUE (requested " 17927a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon + timeSinceRequest + " msec ago)"); 18027a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon return true; 18127a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon } else { 18227a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon if (VDBG) log("isBluetoothAudioConnectedOrPending: ==> FALSE (request too old: " 18327a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon + timeSinceRequest + " msec ago)"); 18427a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon mBluetoothConnectionPending = false; 18527a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon return false; 18627a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon } 18727a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon } 18827a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon 18927a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon if (VDBG) log("isBluetoothAudioConnectedOrPending: ==> FALSE"); 19027a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon return false; 19127a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon } 19227a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon 19327a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon /** 19427a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon * @return true if the onscreen UI should currently be showing the 19527a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon * special "bluetooth is active" indication in a couple of places (in 19627a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon * which UI elements turn blue and/or show the bluetooth logo.) 19727a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon * 19827a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon * This depends on the BluetoothHeadset state *and* the current 19927a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon * telephony state; see shouldShowBluetoothIndication(). 20027a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon * 20127a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon * @see CallCard 20227a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon * @see NotificationMgr.updateInCallNotification 20327a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon */ 20427a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon /* package */ boolean showBluetoothIndication() { 20527a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon return mShowBluetoothIndication; 20627a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon } 20727a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon 20827a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon /** 20927a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon * Recomputes the mShowBluetoothIndication flag based on the current 21027a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon * bluetooth state and current telephony state. 21127a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon * 21227a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon * This needs to be called any time the bluetooth headset state or the 21327a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon * telephony state changes. 21427a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon */ 21527a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon /* package */ void updateBluetoothIndication() { 21627a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon mShowBluetoothIndication = shouldShowBluetoothIndication(mBluetoothHeadsetState, 21727a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon mBluetoothHeadsetAudioState, 21827a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon mCallManager); 21927a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon 22027a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon notifyListeners(mShowBluetoothIndication); 22127a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon } 22227a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon 2239b7bac7705b3afcae7b010edc2032c7c0f37f770Santos Cordon public void addBluetoothIndicatorListener(BluetoothIndicatorListener listener) { 22427a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon if (!mListeners.contains(listener)) { 22527a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon mListeners.add(listener); 22627a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon } 22727a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon } 22827a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon 2299b7bac7705b3afcae7b010edc2032c7c0f37f770Santos Cordon public void removeBluetoothIndicatorListener(BluetoothIndicatorListener listener) { 2309b7bac7705b3afcae7b010edc2032c7c0f37f770Santos Cordon if (mListeners.contains(listener)) { 2319b7bac7705b3afcae7b010edc2032c7c0f37f770Santos Cordon mListeners.remove(listener); 2329b7bac7705b3afcae7b010edc2032c7c0f37f770Santos Cordon } 2339b7bac7705b3afcae7b010edc2032c7c0f37f770Santos Cordon } 2349b7bac7705b3afcae7b010edc2032c7c0f37f770Santos Cordon 23527a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon private void notifyListeners(boolean showBluetoothOn) { 23627a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon for (int i = 0; i < mListeners.size(); i++) { 23727a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon mListeners.get(i).onBluetoothIndicationChange(showBluetoothOn, this); 23827a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon } 23927a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon } 24027a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon 24127a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon private void init(Context context) { 24227a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon Preconditions.checkNotNull(context); 24327a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon 24427a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon if (mBluetoothAdapter != null) { 24527a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon mBluetoothAdapter.getProfileProxy(context, mBluetoothProfileServiceListener, 24627a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon BluetoothProfile.HEADSET); 24727a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon } 24827a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon 24927a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon // Register for misc other intent broadcasts. 25027a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon IntentFilter intentFilter = 25127a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon new IntentFilter(BluetoothHeadset.ACTION_CONNECTION_STATE_CHANGED); 25227a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon intentFilter.addAction(BluetoothHeadset.ACTION_AUDIO_STATE_CHANGED); 25327a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon context.registerReceiver(mReceiver, intentFilter); 25427a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon } 25527a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon 25627a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon private void tearDown() { 25727a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon if (mBluetoothHeadset != null) { 25827a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon mBluetoothAdapter.closeProfileProxy(BluetoothProfile.HEADSET, mBluetoothHeadset); 25927a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon mBluetoothHeadset = null; 26027a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon } 26127a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon } 26227a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon 26327a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon private BluetoothProfile.ServiceListener mBluetoothProfileServiceListener = 26427a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon new BluetoothProfile.ServiceListener() { 26527a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon @Override 26627a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon public void onServiceConnected(int profile, BluetoothProfile proxy) { 26727a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon mBluetoothHeadset = (BluetoothHeadset) proxy; 26827a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon if (VDBG) log("- Got BluetoothHeadset: " + mBluetoothHeadset); 26927a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon } 27027a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon 27127a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon @Override 27227a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon public void onServiceDisconnected(int profile) { 27327a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon mBluetoothHeadset = null; 27427a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon } 27527a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon }; 27627a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon 27727a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon /** 27827a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon * UI policy helper function for the couple of places in the UI that 27927a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon * have some way of indicating that "bluetooth is in use." 28027a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon * 28127a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon * @return true if the onscreen UI should indicate that "bluetooth is in use", 28227a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon * based on the specified bluetooth headset state, and the 28327a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon * current state of the phone. 28427a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon * @see showBluetoothIndication() 28527a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon */ 28627a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon private static boolean shouldShowBluetoothIndication(int bluetoothState, 28727a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon int bluetoothAudioState, 28827a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon CallManager cm) { 28927a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon // We want the UI to indicate that "bluetooth is in use" in two 29027a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon // slightly different cases: 29127a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon // 29227a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon // (a) The obvious case: if a bluetooth headset is currently in 29327a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon // use for an ongoing call. 29427a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon // 29527a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon // (b) The not-so-obvious case: if an incoming call is ringing, 29627a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon // and we expect that audio *will* be routed to a bluetooth 29727a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon // headset once the call is answered. 29827a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon 29927a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon switch (cm.getState()) { 30027a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon case OFFHOOK: 30127a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon // This covers normal active calls, and also the case if 30227a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon // the foreground call is DIALING or ALERTING. In this 30327a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon // case, bluetooth is considered "active" if a headset 30427a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon // is connected *and* audio is being routed to it. 30527a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon return ((bluetoothState == BluetoothHeadset.STATE_CONNECTED) 30627a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon && (bluetoothAudioState == BluetoothHeadset.STATE_AUDIO_CONNECTED)); 30727a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon 30827a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon case RINGING: 30927a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon // If an incoming call is ringing, we're *not* yet routing 31027a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon // audio to the headset (since there's no in-call audio 31127a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon // yet!) In this case, if a bluetooth headset is 31227a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon // connected at all, we assume that it'll become active 31327a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon // once the user answers the phone. 31427a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon return (bluetoothState == BluetoothHeadset.STATE_CONNECTED); 31527a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon 31627a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon default: // Presumably IDLE 31727a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon return false; 31827a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon } 31927a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon } 32027a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon 32127a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon private void dumpBluetoothState() { 32227a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon log("============== dumpBluetoothState() ============="); 32327a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon log("= isBluetoothAvailable: " + isBluetoothAvailable()); 32427a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon log("= isBluetoothAudioConnected: " + isBluetoothAudioConnected()); 32527a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon log("= isBluetoothAudioConnectedOrPending: " + isBluetoothAudioConnectedOrPending()); 32627a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon log("= PhoneApp.showBluetoothIndication: " 32727a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon + showBluetoothIndication()); 32827a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon log("="); 32927a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon if (mBluetoothAdapter != null) { 33027a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon if (mBluetoothHeadset != null) { 33127a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon List<BluetoothDevice> deviceList = mBluetoothHeadset.getConnectedDevices(); 33227a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon 33327a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon if (deviceList.size() > 0) { 33427a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon BluetoothDevice device = deviceList.get(0); 33527a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon log("= BluetoothHeadset.getCurrentDevice: " + device); 33627a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon log("= BluetoothHeadset.State: " 33727a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon + mBluetoothHeadset.getConnectionState(device)); 33827a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon log("= BluetoothHeadset audio connected: " + 33927a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon mBluetoothHeadset.isAudioConnected(device)); 34027a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon } 34127a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon } else { 34227a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon log("= mBluetoothHeadset is null"); 34327a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon } 34427a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon } else { 34527a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon log("= mBluetoothAdapter is null; device is not BT capable"); 34627a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon } 34727a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon } 34827a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon 34927a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon /* package */ void connectBluetoothAudio() { 35027a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon if (VDBG) log("connectBluetoothAudio()..."); 35127a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon if (mBluetoothHeadset != null) { 35227a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon // TODO(BT) check return 35327a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon mBluetoothHeadset.connectAudio(); 35427a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon } 35527a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon 35627a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon // Watch out: The bluetooth connection doesn't happen instantly; 35727a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon // the connectAudio() call returns instantly but does its real 35827a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon // work in another thread. The mBluetoothConnectionPending flag 35927a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon // is just a little trickery to ensure that the onscreen UI updates 36027a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon // instantly. (See isBluetoothAudioConnectedOrPending() above.) 36127a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon mBluetoothConnectionPending = true; 36227a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon mBluetoothConnectionRequestTime = SystemClock.elapsedRealtime(); 36327a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon } 36427a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon 36527a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon /* package */ void disconnectBluetoothAudio() { 36627a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon if (VDBG) log("disconnectBluetoothAudio()..."); 36727a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon if (mBluetoothHeadset != null) { 36827a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon mBluetoothHeadset.disconnectAudio(); 36927a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon } 37027a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon mBluetoothConnectionPending = false; 37127a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon } 37227a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon 37327a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon /** 37427a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon * Receiver for misc intent broadcasts the BluetoothManager cares about. 37527a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon */ 37627a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon private class BluetoothBroadcastReceiver extends BroadcastReceiver { 37727a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon @Override 37827a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon public void onReceive(Context context, Intent intent) { 37927a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon String action = intent.getAction(); 38027a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon 38127a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon if (action.equals(BluetoothHeadset.ACTION_CONNECTION_STATE_CHANGED)) { 38227a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon mBluetoothHeadsetState = intent.getIntExtra(BluetoothHeadset.EXTRA_STATE, 38327a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon BluetoothHeadset.STATE_DISCONNECTED); 38427a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon if (VDBG) Log.d(LOG_TAG, "mReceiver: HEADSET_STATE_CHANGED_ACTION"); 38527a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon if (VDBG) Log.d(LOG_TAG, "==> new state: " + mBluetoothHeadsetState); 38627a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon // Also update any visible UI if necessary 38727a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon updateBluetoothIndication(); 38827a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon } else if (action.equals(BluetoothHeadset.ACTION_AUDIO_STATE_CHANGED)) { 38927a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon mBluetoothHeadsetAudioState = 39027a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon intent.getIntExtra(BluetoothHeadset.EXTRA_STATE, 39127a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon BluetoothHeadset.STATE_AUDIO_DISCONNECTED); 39227a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon if (VDBG) Log.d(LOG_TAG, "mReceiver: HEADSET_AUDIO_STATE_CHANGED_ACTION"); 39327a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon if (VDBG) Log.d(LOG_TAG, "==> new state: " + mBluetoothHeadsetAudioState); 39427a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon updateBluetoothIndication(); 39527a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon } 39627a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon } 39727a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon } 39827a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon 39927a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon private void log(String msg) { 40027a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon Log.d(LOG_TAG, msg); 40127a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon } 40227a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon 40327a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon /* package */ interface BluetoothIndicatorListener { 4049b7bac7705b3afcae7b010edc2032c7c0f37f770Santos Cordon public void onBluetoothIndicationChange(boolean isConnected, BluetoothManager manager); 40527a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon } 40627a3c1f96fab43970e56a5eaa39551a4a248994fSantos Cordon} 407