Adding new BluetoothManager class

Previously, the UI-heavy InCallScreen handled all the bluetooth code.
This code does not add very much logic at all...it's mostly copying from
InCallScreen and whatever Bluetooth code existed in PhoneGlobals.

Changes of note:
- New BluetoothManager class from InCallScreen bluetooth code
- I removed the REQUEST_BLUETOOTH... code from InCallScreen since it was
  there to "update" the UI, which no longer runs.
- Added a Listener to the BluetoothManager for anyone that wants to
  listen in on changes.  This helped remove PhoneGlobals references from
  BluetoothManager code.  Currently PhoneGlobals is the only listener
  (for some proximity sensor-related code) but in the next CL there will
  be an AudioRouter class that will also listen to those changes.
- Previous callers of InCallScreen->whateverBluetooth has been updated
  to use BluetoothManager, or the code was commented because it was
  primarily UI code which is being replaced.

Change-Id: I6f86d16c8bd9cd1da2156c1f3743886f32e84c25
diff --git a/src/com/android/phone/BluetoothManager.java b/src/com/android/phone/BluetoothManager.java
new file mode 100644
index 0000000..a297472
--- /dev/null
+++ b/src/com/android/phone/BluetoothManager.java
@@ -0,0 +1,401 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.phone;
+
+import com.google.android.collect.Lists;
+import com.google.common.base.Preconditions;
+
+import android.bluetooth.BluetoothAdapter;
+import android.bluetooth.BluetoothDevice;
+import android.bluetooth.BluetoothHeadset;
+import android.bluetooth.BluetoothProfile;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.os.Handler;
+import android.os.Message;
+import android.os.SystemClock;
+import android.os.SystemProperties;
+import android.util.Log;
+
+import com.android.internal.telephony.CallManager;
+
+import java.util.List;
+
+/**
+ * Listens to and caches bluetooth headset state.  Used By the CallHandlerServiceProxy
+ * for sending this state to the UI layer.  Also provides method for connection the bluetooth
+ * headset to the phone call.  This is used by the CallCommandService to allow the UI to use the
+ * bluetooth headset if connected.
+ */
+public class BluetoothManager {
+    private static final String LOG_TAG = "InCallScreen";
+    private static final boolean DBG =
+            (PhoneGlobals.DBG_LEVEL >= 1) && (SystemProperties.getInt("ro.debuggable", 0) == 1);
+    private static final boolean VDBG = (PhoneGlobals.DBG_LEVEL >= 2);
+
+    private final BluetoothAdapter mBluetoothAdapter;
+    private final CallManager mCallManager;
+    private final Context mContext;
+
+    private BluetoothHeadset mBluetoothHeadset;
+    private int mBluetoothHeadsetState = BluetoothProfile.STATE_DISCONNECTED;
+    private int mBluetoothHeadsetAudioState = BluetoothHeadset.STATE_AUDIO_DISCONNECTED;
+    private boolean mShowBluetoothIndication = false;
+    private boolean mBluetoothConnectionPending = false;
+    private long mBluetoothConnectionRequestTime;
+
+    // Broadcast receiver for various intent broadcasts (see onCreate())
+    private final BroadcastReceiver mReceiver = new BluetoothBroadcastReceiver();
+
+    private final List<BluetoothIndicatorListener> mListeners = Lists.newArrayList();
+
+    public BluetoothManager(Context context, CallManager callManager) {
+        mContext = context;
+        mCallManager = callManager;
+        mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
+
+        init(mContext);
+        // TODO(klp): Listen for changes to the call list/state.
+    }
+
+    /* package */ boolean isBluetoothHeadsetAudioOn() {
+        return (mBluetoothHeadsetAudioState != BluetoothHeadset.STATE_AUDIO_DISCONNECTED);
+    }
+
+    //
+    // Bluetooth helper methods.
+    //
+    // - BluetoothAdapter is the Bluetooth system service.  If
+    //   getDefaultAdapter() returns null
+    //   then the device is not BT capable.  Use BluetoothDevice.isEnabled()
+    //   to see if BT is enabled on the device.
+    //
+    // - BluetoothHeadset is the API for the control connection to a
+    //   Bluetooth Headset.  This lets you completely connect/disconnect a
+    //   headset (which we don't do from the Phone UI!) but also lets you
+    //   get the address of the currently active headset and see whether
+    //   it's currently connected.
+
+    /**
+     * @return true if the Bluetooth on/off switch in the UI should be
+     *         available to the user (i.e. if the device is BT-capable
+     *         and a headset is connected.)
+     */
+    /* package */ boolean isBluetoothAvailable() {
+        if (VDBG) log("isBluetoothAvailable()...");
+
+        // There's no need to ask the Bluetooth system service if BT is enabled:
+        //
+        //    BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
+        //    if ((adapter == null) || !adapter.isEnabled()) {
+        //        if (DBG) log("  ==> FALSE (BT not enabled)");
+        //        return false;
+        //    }
+        //    if (DBG) log("  - BT enabled!  device name " + adapter.getName()
+        //                 + ", address " + adapter.getAddress());
+        //
+        // ...since we already have a BluetoothHeadset instance.  We can just
+        // call isConnected() on that, and assume it'll be false if BT isn't
+        // enabled at all.
+
+        // Check if there's a connected headset, using the BluetoothHeadset API.
+        boolean isConnected = false;
+        if (mBluetoothHeadset != null) {
+            List<BluetoothDevice> deviceList = mBluetoothHeadset.getConnectedDevices();
+
+            if (deviceList.size() > 0) {
+                BluetoothDevice device = deviceList.get(0);
+                isConnected = true;
+
+                if (VDBG) log("  - headset state = " +
+                              mBluetoothHeadset.getConnectionState(device));
+                if (VDBG) log("  - headset address: " + device);
+                if (VDBG) log("  - isConnected: " + isConnected);
+            }
+        }
+
+        if (VDBG) log("  ==> " + isConnected);
+        return isConnected;
+    }
+
+    /**
+     * @return true if a BT Headset is available, and its audio is currently connected.
+     */
+    /* package */ boolean isBluetoothAudioConnected() {
+        if (mBluetoothHeadset == null) {
+            if (VDBG) log("isBluetoothAudioConnected: ==> FALSE (null mBluetoothHeadset)");
+            return false;
+        }
+        List<BluetoothDevice> deviceList = mBluetoothHeadset.getConnectedDevices();
+
+        if (deviceList.isEmpty()) {
+            return false;
+        }
+        BluetoothDevice device = deviceList.get(0);
+        boolean isAudioOn = mBluetoothHeadset.isAudioConnected(device);
+        if (VDBG) log("isBluetoothAudioConnected: ==> isAudioOn = " + isAudioOn);
+        return isAudioOn;
+    }
+
+    /**
+     * Helper method used to control the onscreen "Bluetooth" indication;
+     * see InCallControlState.bluetoothIndicatorOn.
+     *
+     * @return true if a BT device is available and its audio is currently connected,
+     *              <b>or</b> if we issued a BluetoothHeadset.connectAudio()
+     *              call within the last 5 seconds (which presumably means
+     *              that the BT audio connection is currently being set
+     *              up, and will be connected soon.)
+     */
+    /* package */ boolean isBluetoothAudioConnectedOrPending() {
+        if (isBluetoothAudioConnected()) {
+            if (VDBG) log("isBluetoothAudioConnectedOrPending: ==> TRUE (really connected)");
+            return true;
+        }
+
+        // If we issued a connectAudio() call "recently enough", even
+        // if BT isn't actually connected yet, let's still pretend BT is
+        // on.  This makes the onscreen indication more responsive.
+        if (mBluetoothConnectionPending) {
+            long timeSinceRequest =
+                    SystemClock.elapsedRealtime() - mBluetoothConnectionRequestTime;
+            if (timeSinceRequest < 5000 /* 5 seconds */) {
+                if (VDBG) log("isBluetoothAudioConnectedOrPending: ==> TRUE (requested "
+                             + timeSinceRequest + " msec ago)");
+                return true;
+            } else {
+                if (VDBG) log("isBluetoothAudioConnectedOrPending: ==> FALSE (request too old: "
+                             + timeSinceRequest + " msec ago)");
+                mBluetoothConnectionPending = false;
+                return false;
+            }
+        }
+
+        if (VDBG) log("isBluetoothAudioConnectedOrPending: ==> FALSE");
+        return false;
+    }
+
+    /**
+     * @return true if the onscreen UI should currently be showing the
+     * special "bluetooth is active" indication in a couple of places (in
+     * which UI elements turn blue and/or show the bluetooth logo.)
+     *
+     * This depends on the BluetoothHeadset state *and* the current
+     * telephony state; see shouldShowBluetoothIndication().
+     *
+     * @see CallCard
+     * @see NotificationMgr.updateInCallNotification
+     */
+    /* package */ boolean showBluetoothIndication() {
+        return mShowBluetoothIndication;
+    }
+
+    /**
+     * Recomputes the mShowBluetoothIndication flag based on the current
+     * bluetooth state and current telephony state.
+     *
+     * This needs to be called any time the bluetooth headset state or the
+     * telephony state changes.
+     */
+    /* package */ void updateBluetoothIndication() {
+        mShowBluetoothIndication = shouldShowBluetoothIndication(mBluetoothHeadsetState,
+                                                                 mBluetoothHeadsetAudioState,
+                                                                 mCallManager);
+
+        notifyListeners(mShowBluetoothIndication);
+    }
+
+    /* package */ void addBluetoothIndicatorListener(BluetoothIndicatorListener listener) {
+        if (!mListeners.contains(listener)) {
+            mListeners.add(listener);
+        }
+    }
+
+    private void notifyListeners(boolean showBluetoothOn) {
+        for (int i = 0; i < mListeners.size(); i++) {
+            mListeners.get(i).onBluetoothIndicationChange(showBluetoothOn, this);
+        }
+    }
+
+    private void init(Context context) {
+        Preconditions.checkNotNull(context);
+
+        if (mBluetoothAdapter != null) {
+            mBluetoothAdapter.getProfileProxy(context, mBluetoothProfileServiceListener,
+                                    BluetoothProfile.HEADSET);
+        }
+
+        // Register for misc other intent broadcasts.
+        IntentFilter intentFilter =
+                new IntentFilter(BluetoothHeadset.ACTION_CONNECTION_STATE_CHANGED);
+        intentFilter.addAction(BluetoothHeadset.ACTION_AUDIO_STATE_CHANGED);
+        context.registerReceiver(mReceiver, intentFilter);
+    }
+
+    private void tearDown() {
+        if (mBluetoothHeadset != null) {
+            mBluetoothAdapter.closeProfileProxy(BluetoothProfile.HEADSET, mBluetoothHeadset);
+            mBluetoothHeadset = null;
+        }
+    }
+
+    private BluetoothProfile.ServiceListener mBluetoothProfileServiceListener =
+             new BluetoothProfile.ServiceListener() {
+         @Override
+         public void onServiceConnected(int profile, BluetoothProfile proxy) {
+             mBluetoothHeadset = (BluetoothHeadset) proxy;
+             if (VDBG) log("- Got BluetoothHeadset: " + mBluetoothHeadset);
+         }
+
+         @Override
+         public void onServiceDisconnected(int profile) {
+             mBluetoothHeadset = null;
+         }
+    };
+
+    /**
+     * UI policy helper function for the couple of places in the UI that
+     * have some way of indicating that "bluetooth is in use."
+     *
+     * @return true if the onscreen UI should indicate that "bluetooth is in use",
+     *         based on the specified bluetooth headset state, and the
+     *         current state of the phone.
+     * @see showBluetoothIndication()
+     */
+    private static boolean shouldShowBluetoothIndication(int bluetoothState,
+                                                         int bluetoothAudioState,
+                                                         CallManager cm) {
+        // We want the UI to indicate that "bluetooth is in use" in two
+        // slightly different cases:
+        //
+        // (a) The obvious case: if a bluetooth headset is currently in
+        //     use for an ongoing call.
+        //
+        // (b) The not-so-obvious case: if an incoming call is ringing,
+        //     and we expect that audio *will* be routed to a bluetooth
+        //     headset once the call is answered.
+
+        switch (cm.getState()) {
+            case OFFHOOK:
+                // This covers normal active calls, and also the case if
+                // the foreground call is DIALING or ALERTING.  In this
+                // case, bluetooth is considered "active" if a headset
+                // is connected *and* audio is being routed to it.
+                return ((bluetoothState == BluetoothHeadset.STATE_CONNECTED)
+                        && (bluetoothAudioState == BluetoothHeadset.STATE_AUDIO_CONNECTED));
+
+            case RINGING:
+                // If an incoming call is ringing, we're *not* yet routing
+                // audio to the headset (since there's no in-call audio
+                // yet!)  In this case, if a bluetooth headset is
+                // connected at all, we assume that it'll become active
+                // once the user answers the phone.
+                return (bluetoothState == BluetoothHeadset.STATE_CONNECTED);
+
+            default:  // Presumably IDLE
+                return false;
+        }
+    }
+
+    private void dumpBluetoothState() {
+        log("============== dumpBluetoothState() =============");
+        log("= isBluetoothAvailable: " + isBluetoothAvailable());
+        log("= isBluetoothAudioConnected: " + isBluetoothAudioConnected());
+        log("= isBluetoothAudioConnectedOrPending: " + isBluetoothAudioConnectedOrPending());
+        log("= PhoneApp.showBluetoothIndication: "
+            + showBluetoothIndication());
+        log("=");
+        if (mBluetoothAdapter != null) {
+            if (mBluetoothHeadset != null) {
+                List<BluetoothDevice> deviceList = mBluetoothHeadset.getConnectedDevices();
+
+                if (deviceList.size() > 0) {
+                    BluetoothDevice device = deviceList.get(0);
+                    log("= BluetoothHeadset.getCurrentDevice: " + device);
+                    log("= BluetoothHeadset.State: "
+                        + mBluetoothHeadset.getConnectionState(device));
+                    log("= BluetoothHeadset audio connected: " +
+                        mBluetoothHeadset.isAudioConnected(device));
+                }
+            } else {
+                log("= mBluetoothHeadset is null");
+            }
+        } else {
+            log("= mBluetoothAdapter is null; device is not BT capable");
+        }
+    }
+
+    /* package */ void connectBluetoothAudio() {
+        if (VDBG) log("connectBluetoothAudio()...");
+        if (mBluetoothHeadset != null) {
+            // TODO(BT) check return
+            mBluetoothHeadset.connectAudio();
+        }
+
+        // Watch out: The bluetooth connection doesn't happen instantly;
+        // the connectAudio() call returns instantly but does its real
+        // work in another thread.  The mBluetoothConnectionPending flag
+        // is just a little trickery to ensure that the onscreen UI updates
+        // instantly. (See isBluetoothAudioConnectedOrPending() above.)
+        mBluetoothConnectionPending = true;
+        mBluetoothConnectionRequestTime = SystemClock.elapsedRealtime();
+    }
+
+    /* package */ void disconnectBluetoothAudio() {
+        if (VDBG) log("disconnectBluetoothAudio()...");
+        if (mBluetoothHeadset != null) {
+            mBluetoothHeadset.disconnectAudio();
+        }
+        mBluetoothConnectionPending = false;
+    }
+
+    /**
+     * Receiver for misc intent broadcasts the BluetoothManager cares about.
+     */
+    private class BluetoothBroadcastReceiver extends BroadcastReceiver {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            String action = intent.getAction();
+
+            if (action.equals(BluetoothHeadset.ACTION_CONNECTION_STATE_CHANGED)) {
+                mBluetoothHeadsetState = intent.getIntExtra(BluetoothHeadset.EXTRA_STATE,
+                                                          BluetoothHeadset.STATE_DISCONNECTED);
+                if (VDBG) Log.d(LOG_TAG, "mReceiver: HEADSET_STATE_CHANGED_ACTION");
+                if (VDBG) Log.d(LOG_TAG, "==> new state: " + mBluetoothHeadsetState);
+                // Also update any visible UI if necessary
+                updateBluetoothIndication();
+            } else if (action.equals(BluetoothHeadset.ACTION_AUDIO_STATE_CHANGED)) {
+                mBluetoothHeadsetAudioState =
+                        intent.getIntExtra(BluetoothHeadset.EXTRA_STATE,
+                                           BluetoothHeadset.STATE_AUDIO_DISCONNECTED);
+                if (VDBG) Log.d(LOG_TAG, "mReceiver: HEADSET_AUDIO_STATE_CHANGED_ACTION");
+                if (VDBG) Log.d(LOG_TAG, "==> new state: " + mBluetoothHeadsetAudioState);
+                updateBluetoothIndication();
+            }
+        }
+    }
+
+    private void log(String msg) {
+        Log.d(LOG_TAG, msg);
+    }
+
+    /* package */ interface BluetoothIndicatorListener {
+        public void onBluetoothIndicationChange(boolean showAsConnected, BluetoothManager manager);
+    }
+}
diff --git a/src/com/android/phone/CallCard.java b/src/com/android/phone/CallCard.java
index 682113f..912af73 100644
--- a/src/com/android/phone/CallCard.java
+++ b/src/com/android/phone/CallCard.java
@@ -782,9 +782,10 @@
                 // Also, display a special icon (alongside the "Incoming call"
                 // label) if there's an incoming call and audio will be routed
                 // to bluetooth when you answer it.
-                if (mApplication.showBluetoothIndication()) {
-                    bluetoothIconId = R.drawable.ic_incoming_call_bluetooth;
-                }
+                // TODO(klp): Add bluetooth label to new UI screen for incoming calls.
+                //if (mApplication.showBluetoothIndication()) {
+                //    bluetoothIconId = R.drawable.ic_incoming_call_bluetooth;
+                //}
                 break;
 
             case DISCONNECTING:
diff --git a/src/com/android/phone/CallNotifier.java b/src/com/android/phone/CallNotifier.java
index 669f9b0..31b0c66 100644
--- a/src/com/android/phone/CallNotifier.java
+++ b/src/com/android/phone/CallNotifier.java
@@ -167,15 +167,19 @@
     // Cached AudioManager
     private AudioManager mAudioManager;
 
+    private final BluetoothManager mBluetoothManager;
+
     /**
      * Initialize the singleton CallNotifier instance.
      * This is only done once, at startup, from PhoneApp.onCreate().
      */
     /* package */ static CallNotifier init(PhoneGlobals app, Phone phone, Ringer ringer,
-            CallLogger callLogger, CallStateMonitor callStateMonitor) {
+            CallLogger callLogger, CallStateMonitor callStateMonitor,
+            BluetoothManager bluetoothManager) {
         synchronized (CallNotifier.class) {
             if (sInstance == null) {
-                sInstance = new CallNotifier(app, phone, ringer, callLogger, callStateMonitor);
+                sInstance = new CallNotifier(app, phone, ringer, callLogger, callStateMonitor,
+                        bluetoothManager);
             } else {
                 Log.wtf(LOG_TAG, "init() called multiple times!  sInstance = " + sInstance);
             }
@@ -185,11 +189,12 @@
 
     /** Private constructor; @see init() */
     private CallNotifier(PhoneGlobals app, Phone phone, Ringer ringer, CallLogger callLogger,
-            CallStateMonitor callStateMonitor) {
+            CallStateMonitor callStateMonitor, BluetoothManager bluetoothManager) {
         mApplication = app;
         mCM = app.mCM;
         mCallLogger = callLogger;
         mCallStateMonitor = callStateMonitor;
+        mBluetoothManager = bluetoothManager;
 
         mAudioManager = (AudioManager) mApplication.getSystemService(Context.AUDIO_SERVICE);
 
@@ -753,7 +758,9 @@
         // There's no need to force a UI update since we update the
         // in-call notification ourselves (below), and the InCallScreen
         // listens for phone state changes itself.
-        mApplication.updateBluetoothIndication(false);
+        // TODO(klp): Have BluetoothManager listen to CallModeler instead of relying on
+        // CallNotifier
+        mBluetoothManager.updateBluetoothIndication();
 
 
         // Update the phone state and other sensor/lock.
diff --git a/src/com/android/phone/InCallControlState.java b/src/com/android/phone/InCallControlState.java
index d21343d..38eaf5c 100644
--- a/src/com/android/phone/InCallControlState.java
+++ b/src/com/android/phone/InCallControlState.java
@@ -43,6 +43,7 @@
     private static final String LOG_TAG = "InCallControlState";
     private static final boolean DBG = (PhoneGlobals.DBG_LEVEL >= 2);
 
+    private final BluetoothManager mBluetoothManager;
     private InCallScreen mInCallScreen;
     private CallManager mCM;
 
@@ -85,10 +86,12 @@
     public boolean canHold;
 
 
-    public InCallControlState(InCallScreen inCallScreen, CallManager cm) {
+    public InCallControlState(InCallScreen inCallScreen, CallManager cm,
+            BluetoothManager bluetoothManager) {
         if (DBG) log("InCallControlState constructor...");
         mInCallScreen = inCallScreen;
         mCM = cm;
+        mBluetoothManager = bluetoothManager;
     }
 
     /**
@@ -132,9 +135,9 @@
         canMerge = PhoneUtils.okToMergeCalls(mCM);
 
         // "Bluetooth":
-        if (mInCallScreen.isBluetoothAvailable()) {
+        if (mBluetoothManager.isBluetoothAvailable()) {
             bluetoothEnabled = true;
-            bluetoothIndicatorOn = mInCallScreen.isBluetoothAudioConnectedOrPending();
+            bluetoothIndicatorOn = mBluetoothManager.isBluetoothAudioConnectedOrPending();
         } else {
             bluetoothEnabled = false;
             bluetoothIndicatorOn = false;
diff --git a/src/com/android/phone/InCallScreen.java b/src/com/android/phone/InCallScreen.java
index d5db689..f20406b 100644
--- a/src/com/android/phone/InCallScreen.java
+++ b/src/com/android/phone/InCallScreen.java
@@ -153,7 +153,6 @@
     private static final int DONT_ADD_VOICEMAIL_NUMBER = 107;
     private static final int DELAYED_CLEANUP_AFTER_DISCONNECT = 108;
     private static final int SUPP_SERVICE_FAILED = 110;
-    private static final int REQUEST_UPDATE_BLUETOOTH_INDICATION = 114;
     private static final int PHONE_CDMA_CALL_WAITING = 115;
     private static final int REQUEST_CLOSE_SPC_ERROR_NOTICE = 118;
     private static final int REQUEST_CLOSE_OTA_FAILURE_NOTICE = 119;
@@ -198,11 +197,6 @@
     // based on the current foreground Call.)
     private Phone mPhone;
 
-    private BluetoothHeadset mBluetoothHeadset;
-    private BluetoothAdapter mBluetoothAdapter;
-    private boolean mBluetoothConnectionPending;
-    private long mBluetoothConnectionRequestTime;
-
     /** Main in-call UI elements. */
     private CallCard mCallCard;
 
@@ -344,16 +338,6 @@
                     delayedCleanupAfterDisconnect();
                     break;
 
-                case REQUEST_UPDATE_BLUETOOTH_INDICATION:
-                    if (VDBG) log("REQUEST_UPDATE_BLUETOOTH_INDICATION...");
-                    // The bluetooth headset state changed, so some UI
-                    // elements may need to update.  (There's no need to
-                    // look up the current state here, since any UI
-                    // elements that care about the bluetooth state get it
-                    // directly from PhoneApp.showBluetoothIndication().)
-                    updateScreen();
-                    break;
-
                 case PHONE_CDMA_CALL_WAITING:
                     if (DBG) log("Received PHONE_CDMA_CALL_WAITING event ...");
                     Connection cn = mCM.getFirstActiveRingingCall().getLatestConnection();
@@ -480,12 +464,6 @@
         mCM =  mApp.mCM;
         log("- onCreate: phone state = " + mCM.getState());
 
-        mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
-        if (mBluetoothAdapter != null) {
-            mBluetoothAdapter.getProfileProxy(getApplicationContext(), mBluetoothProfileServiceListener,
-                                    BluetoothProfile.HEADSET);
-        }
-
         requestWindowFeature(Window.FEATURE_NO_TITLE);
 
         // Inflate everything in incall_screen.xml and add it to the screen.
@@ -519,20 +497,6 @@
         if (DBG) log("onCreate(): exit");
     }
 
-     private BluetoothProfile.ServiceListener mBluetoothProfileServiceListener =
-             new BluetoothProfile.ServiceListener() {
-         @Override
-         public void onServiceConnected(int profile, BluetoothProfile proxy) {
-             mBluetoothHeadset = (BluetoothHeadset) proxy;
-             if (VDBG) log("- Got BluetoothHeadset: " + mBluetoothHeadset);
-         }
-
-         @Override
-         public void onServiceDisconnected(int profile) {
-             mBluetoothHeadset = null;
-         }
-    };
-
     /**
      * Sets the Phone object used internally by the InCallScreen.
      *
@@ -609,7 +573,6 @@
         // most common example of this is when there's some kind of
         // failure while initiating an outgoing call; see
         // CallController.placeCall().)
-        //
         boolean handledStartupError = false;
         if (inCallUiState.hasPendingCallStatusCode()) {
             if (DBG) log("- onResume: need to show status indication!");
@@ -622,8 +585,9 @@
         }
 
         // Set the volume control handler while we are in the foreground.
-        final boolean bluetoothConnected = isBluetoothAudioConnected();
+        final boolean bluetoothConnected = false; //isBluetoothAudioConnected();
 
+        // TODO(klp): Move this volume button control code to the UI
         if (bluetoothConnected) {
             setVolumeControlStream(AudioManager.STREAM_BLUETOOTH_SCO);
         } else {
@@ -939,11 +903,6 @@
         // No need to change wake state here; that happens in onPause() when we
         // are moving out of the foreground.
 
-        if (mBluetoothHeadset != null) {
-            mBluetoothAdapter.closeProfileProxy(BluetoothProfile.HEADSET, mBluetoothHeadset);
-            mBluetoothHeadset = null;
-        }
-
         // Dismiss all dialogs, to be absolutely sure we won't leak any of
         // them while changing orientation.
         dismissAllDialogs();
@@ -1254,7 +1213,7 @@
         initInCallTouchUi();
 
         // Helper class to keep track of enabledness/state of UI controls
-        mInCallControlState = new InCallControlState(this, mCM);
+        mInCallControlState = new InCallControlState(this, mCM, mApp.getBluetoothManager());
 
         // Helper class to run the "Manage conference" UI
         mManageConferenceUtils = new ManageConferenceUtils(this, mCM);
@@ -2453,7 +2412,6 @@
         boolean updateSuccessful = false;
         if (DBG) log("syncWithPhoneState()...");
         if (DBG) PhoneUtils.dumpCallState(mPhone);
-        if (VDBG) dumpBluetoothState();
 
         // Make sure the Phone is "in use".  (If not, we shouldn't be on
         // this screen in the first place.)
@@ -2785,9 +2743,12 @@
         boolean newSpeakerState = !PhoneUtils.isSpeakerOn(this);
         log("toggleSpeaker(): newSpeakerState = " + newSpeakerState);
 
-        if (newSpeakerState && isBluetoothAvailable() && isBluetoothAudioConnected()) {
+        // TODO(klp): Add bluetooth query back in for AudioRouter.
+        /*
+         if (newSpeakerState && isBluetoothAvailable() && isBluetoothAudioConnected()) {
             disconnectBluetoothAudio();
         }
+        */
         PhoneUtils.turnOnSpeaker(this, newSpeakerState, true);
 
         // And update the InCallTouchUi widget (since the "audio mode"
@@ -2818,6 +2779,9 @@
     public void toggleBluetooth() {
         if (VDBG) log("toggleBluetooth()...");
 
+        // TODO(klp): Copy to new AudioManager for new incall
+
+        /*
         if (isBluetoothAvailable()) {
             // Toggle the bluetooth audio connection state:
             if (isBluetoothAudioConnected()) {
@@ -2846,6 +2810,7 @@
         // button might need to change its appearance based on the new
         // audio state.)
         updateInCallTouchUi();
+        */
     }
 
     /**
@@ -2861,6 +2826,9 @@
      * we can get rid of toggleBluetooth() and toggleSpeaker().
      */
     public void switchInCallAudio(InCallAudioMode newMode) {
+        // TODO(klp): Copy to new AudioManager for new incall
+        /*
+
         log("switchInCallAudio: new mode = " + newMode);
         switch (newMode) {
             case SPEAKER:
@@ -2910,6 +2878,7 @@
         // mode" button might need to change its appearance based on the
         // new audio state.)
         updateInCallTouchUi();
+        */
     }
 
     /**
@@ -4056,185 +4025,6 @@
                 (mApp.getKeyguardManager().inKeyguardRestrictedInputMode()));
     }
 
-
-    //
-    // Bluetooth helper methods.
-    //
-    // - BluetoothAdapter is the Bluetooth system service.  If
-    //   getDefaultAdapter() returns null
-    //   then the device is not BT capable.  Use BluetoothDevice.isEnabled()
-    //   to see if BT is enabled on the device.
-    //
-    // - BluetoothHeadset is the API for the control connection to a
-    //   Bluetooth Headset.  This lets you completely connect/disconnect a
-    //   headset (which we don't do from the Phone UI!) but also lets you
-    //   get the address of the currently active headset and see whether
-    //   it's currently connected.
-
-    /**
-     * @return true if the Bluetooth on/off switch in the UI should be
-     *         available to the user (i.e. if the device is BT-capable
-     *         and a headset is connected.)
-     */
-    /* package */ boolean isBluetoothAvailable() {
-        if (VDBG) log("isBluetoothAvailable()...");
-
-        // There's no need to ask the Bluetooth system service if BT is enabled:
-        //
-        //    BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
-        //    if ((adapter == null) || !adapter.isEnabled()) {
-        //        if (DBG) log("  ==> FALSE (BT not enabled)");
-        //        return false;
-        //    }
-        //    if (DBG) log("  - BT enabled!  device name " + adapter.getName()
-        //                 + ", address " + adapter.getAddress());
-        //
-        // ...since we already have a BluetoothHeadset instance.  We can just
-        // call isConnected() on that, and assume it'll be false if BT isn't
-        // enabled at all.
-
-        // Check if there's a connected headset, using the BluetoothHeadset API.
-        boolean isConnected = false;
-        if (mBluetoothHeadset != null) {
-            List<BluetoothDevice> deviceList = mBluetoothHeadset.getConnectedDevices();
-
-            if (deviceList.size() > 0) {
-                BluetoothDevice device = deviceList.get(0);
-                isConnected = true;
-
-                if (VDBG) log("  - headset state = " +
-                              mBluetoothHeadset.getConnectionState(device));
-                if (VDBG) log("  - headset address: " + device);
-                if (VDBG) log("  - isConnected: " + isConnected);
-            }
-        }
-
-        if (VDBG) log("  ==> " + isConnected);
-        return isConnected;
-    }
-
-    /**
-     * @return true if a BT Headset is available, and its audio is currently connected.
-     */
-    /* package */ boolean isBluetoothAudioConnected() {
-        if (mBluetoothHeadset == null) {
-            if (VDBG) log("isBluetoothAudioConnected: ==> FALSE (null mBluetoothHeadset)");
-            return false;
-        }
-        List<BluetoothDevice> deviceList = mBluetoothHeadset.getConnectedDevices();
-
-        if (deviceList.isEmpty()) {
-            return false;
-        }
-        BluetoothDevice device = deviceList.get(0);
-        boolean isAudioOn = mBluetoothHeadset.isAudioConnected(device);
-        if (VDBG) log("isBluetoothAudioConnected: ==> isAudioOn = " + isAudioOn);
-        return isAudioOn;
-    }
-
-    /**
-     * Helper method used to control the onscreen "Bluetooth" indication;
-     * see InCallControlState.bluetoothIndicatorOn.
-     *
-     * @return true if a BT device is available and its audio is currently connected,
-     *              <b>or</b> if we issued a BluetoothHeadset.connectAudio()
-     *              call within the last 5 seconds (which presumably means
-     *              that the BT audio connection is currently being set
-     *              up, and will be connected soon.)
-     */
-    /* package */ boolean isBluetoothAudioConnectedOrPending() {
-        if (isBluetoothAudioConnected()) {
-            if (VDBG) log("isBluetoothAudioConnectedOrPending: ==> TRUE (really connected)");
-            return true;
-        }
-
-        // If we issued a connectAudio() call "recently enough", even
-        // if BT isn't actually connected yet, let's still pretend BT is
-        // on.  This makes the onscreen indication more responsive.
-        if (mBluetoothConnectionPending) {
-            long timeSinceRequest =
-                    SystemClock.elapsedRealtime() - mBluetoothConnectionRequestTime;
-            if (timeSinceRequest < 5000 /* 5 seconds */) {
-                if (VDBG) log("isBluetoothAudioConnectedOrPending: ==> TRUE (requested "
-                             + timeSinceRequest + " msec ago)");
-                return true;
-            } else {
-                if (VDBG) log("isBluetoothAudioConnectedOrPending: ==> FALSE (request too old: "
-                             + timeSinceRequest + " msec ago)");
-                mBluetoothConnectionPending = false;
-                return false;
-            }
-        }
-
-        if (VDBG) log("isBluetoothAudioConnectedOrPending: ==> FALSE");
-        return false;
-    }
-
-    /**
-     * Posts a message to our handler saying to update the onscreen UI
-     * based on a bluetooth headset state change.
-     */
-    /* package */ void requestUpdateBluetoothIndication() {
-        if (VDBG) log("requestUpdateBluetoothIndication()...");
-        // No need to look at the current state here; any UI elements that
-        // care about the bluetooth state (i.e. the CallCard) get
-        // the necessary state directly from PhoneApp.showBluetoothIndication().
-        mHandler.removeMessages(REQUEST_UPDATE_BLUETOOTH_INDICATION);
-        mHandler.sendEmptyMessage(REQUEST_UPDATE_BLUETOOTH_INDICATION);
-    }
-
-    private void dumpBluetoothState() {
-        log("============== dumpBluetoothState() =============");
-        log("= isBluetoothAvailable: " + isBluetoothAvailable());
-        log("= isBluetoothAudioConnected: " + isBluetoothAudioConnected());
-        log("= isBluetoothAudioConnectedOrPending: " + isBluetoothAudioConnectedOrPending());
-        log("= PhoneApp.showBluetoothIndication: "
-            + mApp.showBluetoothIndication());
-        log("=");
-        if (mBluetoothAdapter != null) {
-            if (mBluetoothHeadset != null) {
-                List<BluetoothDevice> deviceList = mBluetoothHeadset.getConnectedDevices();
-
-                if (deviceList.size() > 0) {
-                    BluetoothDevice device = deviceList.get(0);
-                    log("= BluetoothHeadset.getCurrentDevice: " + device);
-                    log("= BluetoothHeadset.State: "
-                        + mBluetoothHeadset.getConnectionState(device));
-                    log("= BluetoothHeadset audio connected: " +
-                        mBluetoothHeadset.isAudioConnected(device));
-                }
-            } else {
-                log("= mBluetoothHeadset is null");
-            }
-        } else {
-            log("= mBluetoothAdapter is null; device is not BT capable");
-        }
-    }
-
-    /* package */ void connectBluetoothAudio() {
-        if (VDBG) log("connectBluetoothAudio()...");
-        if (mBluetoothHeadset != null) {
-            // TODO(BT) check return
-            mBluetoothHeadset.connectAudio();
-        }
-
-        // Watch out: The bluetooth connection doesn't happen instantly;
-        // the connectAudio() call returns instantly but does its real
-        // work in another thread.  The mBluetoothConnectionPending flag
-        // is just a little trickery to ensure that the onscreen UI updates
-        // instantly. (See isBluetoothAudioConnectedOrPending() above.)
-        mBluetoothConnectionPending = true;
-        mBluetoothConnectionRequestTime = SystemClock.elapsedRealtime();
-    }
-
-    /* package */ void disconnectBluetoothAudio() {
-        if (VDBG) log("disconnectBluetoothAudio()...");
-        if (mBluetoothHeadset != null) {
-            mBluetoothHeadset.disconnectAudio();
-        }
-        mBluetoothConnectionPending = false;
-    }
-
     /**
      * Posts a handler message telling the InCallScreen to close
      * the OTA failure notice after the specified delay.
diff --git a/src/com/android/phone/OtaUtils.java b/src/com/android/phone/OtaUtils.java
index 495df27..a59c2e6 100644
--- a/src/com/android/phone/OtaUtils.java
+++ b/src/com/android/phone/OtaUtils.java
@@ -168,6 +168,8 @@
     // an InCallScreen or CallCard or any OTASP UI elements at all.
     private boolean mInteractive = true;
 
+    // used when setting speakerphone
+    private final BluetoothManager mBluetoothManager;
 
     /**
      * OtaWidgetData class represent all OTA UI elements
@@ -214,11 +216,12 @@
      * Note if interactive is true, you must also call updateUiWidgets() as soon
      * as the InCallScreen instance is ready.
      */
-    public OtaUtils(Context context, boolean interactive) {
+    public OtaUtils(Context context, boolean interactive, BluetoothManager bluetoothManager) {
         if (DBG) log("OtaUtils constructor...");
         mApplication = PhoneGlobals.getInstance();
         mContext = context;
         mInteractive = interactive;
+        mBluetoothManager = bluetoothManager;
     }
 
     /**
@@ -442,7 +445,8 @@
         }
 
         // Create the OtaUtils instance.
-        app.otaUtils = new OtaUtils(context, false /* non-interactive mode */);
+        app.otaUtils = new OtaUtils(context, false /* non-interactive mode */,
+                app.getBluetoothManager());
         if (DBG) log("- created OtaUtils: " + app.otaUtils);
 
         // ... and kick off the OTASP call.
@@ -558,7 +562,8 @@
         }
 
         // Create the OtaUtils instance.
-        app.otaUtils = new OtaUtils(app.getApplicationContext(), true /* interactive */);
+        app.otaUtils = new OtaUtils(app.getApplicationContext(), true /* interactive */,
+                app.getBluetoothManager());
         if (DBG) log("- created OtaUtils: " + app.otaUtils);
 
         // NOTE we still need to call OtaUtils.updateUiWidgets() once the
@@ -616,9 +621,9 @@
             return;
         }
 
-        if (state && mInCallScreen.isBluetoothAvailable()
-                && mInCallScreen.isBluetoothAudioConnected()) {
-            mInCallScreen.disconnectBluetoothAudio();
+        if (state && mBluetoothManager.isBluetoothAvailable()
+                && mBluetoothManager.isBluetoothAudioConnected()) {
+            mBluetoothManager.disconnectBluetoothAudio();
         }
         PhoneUtils.turnOnSpeaker(mContext, state, true);
     }
diff --git a/src/com/android/phone/PhoneGlobals.java b/src/com/android/phone/PhoneGlobals.java
index b3a01e2..9db596b 100644
--- a/src/com/android/phone/PhoneGlobals.java
+++ b/src/com/android/phone/PhoneGlobals.java
@@ -21,8 +21,6 @@
 import android.app.PendingIntent;
 import android.app.ProgressDialog;
 import android.bluetooth.BluetoothAdapter;
-import android.bluetooth.BluetoothHeadset;
-import android.bluetooth.BluetoothProfile;
 import android.bluetooth.IBluetoothHeadsetPhone;
 import android.content.ActivityNotFoundException;
 import android.content.BroadcastReceiver;
@@ -69,6 +67,7 @@
 import com.android.internal.telephony.TelephonyIntents;
 import com.android.internal.telephony.cdma.TtyIntent;
 import com.android.phone.common.CallLogAsync;
+import com.android.phone.BluetoothManager.BluetoothIndicatorListener;
 import com.android.phone.OtaUtils.CdmaOtaScreenState;
 import com.android.server.sip.SipService;
 
@@ -77,7 +76,7 @@
  * phone process.
  */
 public class PhoneGlobals extends ContextWrapper
-        implements AccelerometerListener.OrientationListener {
+        implements AccelerometerListener.OrientationListener, BluetoothIndicatorListener {
     /* package */ static final String LOG_TAG = "PhoneApp";
 
     /**
@@ -170,6 +169,7 @@
     Phone phone;
     PhoneInterfaceManager phoneMgr;
 
+    private BluetoothManager bluetoothManager;
     private CallCommandService callCommandService;
     private CallHandlerServiceProxy callHandlerServiceProxy;
     private CallModeler callModeler;
@@ -178,10 +178,6 @@
     private IBluetoothHeadsetPhone mBluetoothPhone;
     private Ringer ringer;
 
-    private int mBluetoothHeadsetState = BluetoothProfile.STATE_DISCONNECTED;
-    private int mBluetoothHeadsetAudioState = BluetoothHeadset.STATE_AUDIO_DISCONNECTED;
-    private boolean mShowBluetoothIndication = false;
-
     static int mDockState = Intent.EXTRA_DOCK_STATE_UNDOCKED;
     static boolean sVoiceCapable = true;
 
@@ -347,7 +343,8 @@
 
                     phoneState = mCM.getState();
                     // Do not change speaker state if phone is not off hook
-                    if (phoneState == PhoneConstants.State.OFFHOOK && !isBluetoothHeadsetAudioOn()) {
+                    if (phoneState == PhoneConstants.State.OFFHOOK &&
+                            !bluetoothManager.isBluetoothHeadsetAudioOn()) {
                         if (!isHeadsetPlugged()) {
                             // if the state is "not connected", restore the speaker state.
                             PhoneUtils.restoreSpeakerMode(getApplicationContext());
@@ -401,7 +398,7 @@
 
                     phoneState = mCM.getState();
                     if (phoneState == PhoneConstants.State.OFFHOOK &&
-                        !isHeadsetPlugged() && !isBluetoothHeadsetAudioOn()) {
+                        !isHeadsetPlugged() && !bluetoothManager.isBluetoothHeadsetAudioOn()) {
                         PhoneUtils.turnOnSpeaker(getApplicationContext(), inDockMode, true);
                         updateInCallScreen();  // Has no effect if the InCallScreen isn't visible
                     }
@@ -490,7 +487,11 @@
                 mBluetoothPhone = null;
             }
 
-            ringer = Ringer.init(this);
+            // Bluetooth manager
+            bluetoothManager = new BluetoothManager(this, mCM);
+            bluetoothManager.addBluetoothIndicatorListener(this);
+
+            ringer = Ringer.init(this, bluetoothManager);
 
             // before registering for phone state changes
             mPowerManager = (PowerManager) getSystemService(Context.POWER_SERVICE);
@@ -560,7 +561,8 @@
             // asynchronous events from the telephony layer (like
             // launching the incoming-call UI when an incoming call comes
             // in.)
-            notifier = CallNotifier.init(this, phone, ringer, callLogger, callStateMonitor);
+            notifier = CallNotifier.init(this, phone, ringer, callLogger, callStateMonitor,
+                    bluetoothManager);
 
             // register for ICC status
             IccCard sim = phone.getIccCard();
@@ -581,8 +583,6 @@
             // Register for misc other intent broadcasts.
             IntentFilter intentFilter =
                     new IntentFilter(Intent.ACTION_AIRPLANE_MODE_CHANGED);
-            intentFilter.addAction(BluetoothHeadset.ACTION_CONNECTION_STATE_CHANGED);
-            intentFilter.addAction(BluetoothHeadset.ACTION_AUDIO_STATE_CHANGED);
             intentFilter.addAction(TelephonyIntents.ACTION_ANY_DATA_CONNECTION_STATE_CHANGED);
             intentFilter.addAction(Intent.ACTION_HEADSET_PLUG);
             intentFilter.addAction(Intent.ACTION_DOCK_EVENT);
@@ -708,8 +708,8 @@
         return mBluetoothPhone;
     }
 
-    boolean isBluetoothHeadsetAudioOn() {
-        return (mBluetoothHeadsetAudioState != BluetoothHeadset.STATE_AUDIO_DISCONNECTED);
+    /* package */ BluetoothManager getBluetoothManager() {
+        return bluetoothManager;
     }
 
     /**
@@ -1169,7 +1169,7 @@
                 // is being held in a horizontal position.
                 boolean screenOnImmediately = (isHeadsetPlugged()
                                                || PhoneUtils.isSpeakerOn(this)
-                                               || isBluetoothHeadsetAudioOn()
+                                               || bluetoothManager.isBluetoothHeadsetAudioOn()
                                                || mIsHardKeyboardOpen);
 
                 // We do not keep the screen off when the user is outside in-call screen and we are
@@ -1371,92 +1371,19 @@
     }
 
     /**
-     * @return true if the onscreen UI should currently be showing the
-     * special "bluetooth is active" indication in a couple of places (in
-     * which UI elements turn blue and/or show the bluetooth logo.)
-     *
-     * This depends on the BluetoothHeadset state *and* the current
-     * telephony state; see shouldShowBluetoothIndication().
-     *
-     * @see CallCard
-     * @see NotificationMgr.updateInCallNotification
-     */
-    /* package */ boolean showBluetoothIndication() {
-        return mShowBluetoothIndication;
-    }
-
-    /**
-     * Recomputes the mShowBluetoothIndication flag based on the current
-     * bluetooth state and current telephony state.
-     *
      * This needs to be called any time the bluetooth headset state or the
      * telephony state changes.
-     *
-     * @param forceUiUpdate if true, force the UI elements that care
-     *                      about this flag to update themselves.
+     * TODO(klp): See about a ProximityManager-type class listening to bluetooth
+     * state changes instead of having this global method
      */
-    /* package */ void updateBluetoothIndication(boolean forceUiUpdate) {
-        mShowBluetoothIndication = shouldShowBluetoothIndication(mBluetoothHeadsetState,
-                                                                 mBluetoothHeadsetAudioState,
-                                                                 mCM);
-        if (forceUiUpdate) {
-            // Post Handler messages to the various components that might
-            // need to be refreshed based on the new state.
-            if (isShowingCallScreen()) mInCallScreen.requestUpdateBluetoothIndication();
-            if (DBG) Log.d (LOG_TAG, "- updating in-call notification for BT state change...");
-            mHandler.sendEmptyMessage(EVENT_UPDATE_INCALL_NOTIFICATION);
-        }
 
+    @Override
+    public void onBluetoothIndicationChange(boolean showAsConnected, BluetoothManager manager) {
         // Update the Proximity sensor based on Bluetooth audio state
         updateProximitySensorMode(mCM.getState());
     }
 
     /**
-     * UI policy helper function for the couple of places in the UI that
-     * have some way of indicating that "bluetooth is in use."
-     *
-     * @return true if the onscreen UI should indicate that "bluetooth is in use",
-     *         based on the specified bluetooth headset state, and the
-     *         current state of the phone.
-     * @see showBluetoothIndication()
-     */
-    private static boolean shouldShowBluetoothIndication(int bluetoothState,
-                                                         int bluetoothAudioState,
-                                                         CallManager cm) {
-        // We want the UI to indicate that "bluetooth is in use" in two
-        // slightly different cases:
-        //
-        // (a) The obvious case: if a bluetooth headset is currently in
-        //     use for an ongoing call.
-        //
-        // (b) The not-so-obvious case: if an incoming call is ringing,
-        //     and we expect that audio *will* be routed to a bluetooth
-        //     headset once the call is answered.
-
-        switch (cm.getState()) {
-            case OFFHOOK:
-                // This covers normal active calls, and also the case if
-                // the foreground call is DIALING or ALERTING.  In this
-                // case, bluetooth is considered "active" if a headset
-                // is connected *and* audio is being routed to it.
-                return ((bluetoothState == BluetoothHeadset.STATE_CONNECTED)
-                        && (bluetoothAudioState == BluetoothHeadset.STATE_AUDIO_CONNECTED));
-
-            case RINGING:
-                // If an incoming call is ringing, we're *not* yet routing
-                // audio to the headset (since there's no in-call audio
-                // yet!)  In this case, if a bluetooth headset is
-                // connected at all, we assume that it'll become active
-                // once the user answers the phone.
-                return (bluetoothState == BluetoothHeadset.STATE_CONNECTED);
-
-            default:  // Presumably IDLE
-                return false;
-        }
-    }
-
-
-    /**
      * Receiver for misc intent broadcasts the Phone app cares about.
      */
     private class PhoneAppBroadcastReceiver extends BroadcastReceiver {
@@ -1467,19 +1394,6 @@
                 boolean enabled = System.getInt(getContentResolver(),
                         System.AIRPLANE_MODE_ON, 0) == 0;
                 phone.setRadioPower(enabled);
-            } else if (action.equals(BluetoothHeadset.ACTION_CONNECTION_STATE_CHANGED)) {
-                mBluetoothHeadsetState = intent.getIntExtra(BluetoothHeadset.EXTRA_STATE,
-                                                          BluetoothHeadset.STATE_DISCONNECTED);
-                if (VDBG) Log.d(LOG_TAG, "mReceiver: HEADSET_STATE_CHANGED_ACTION");
-                if (VDBG) Log.d(LOG_TAG, "==> new state: " + mBluetoothHeadsetState);
-                updateBluetoothIndication(true);  // Also update any visible UI if necessary
-            } else if (action.equals(BluetoothHeadset.ACTION_AUDIO_STATE_CHANGED)) {
-                mBluetoothHeadsetAudioState =
-                        intent.getIntExtra(BluetoothHeadset.EXTRA_STATE,
-                                           BluetoothHeadset.STATE_AUDIO_DISCONNECTED);
-                if (VDBG) Log.d(LOG_TAG, "mReceiver: HEADSET_AUDIO_STATE_CHANGED_ACTION");
-                if (VDBG) Log.d(LOG_TAG, "==> new state: " + mBluetoothHeadsetAudioState);
-                updateBluetoothIndication(true);  // Also update any visible UI if necessary
             } else if (action.equals(TelephonyIntents.ACTION_ANY_DATA_CONNECTION_STATE_CHANGED)) {
                 if (VDBG) Log.d(LOG_TAG, "mReceiver: ACTION_ANY_DATA_CONNECTION_STATE_CHANGED");
                 if (VDBG) Log.d(LOG_TAG, "- state: " + intent.getStringExtra(PhoneConstants.STATE_KEY));
diff --git a/src/com/android/phone/PhoneUtils.java b/src/com/android/phone/PhoneUtils.java
index a64a205..f243123 100644
--- a/src/com/android/phone/PhoneUtils.java
+++ b/src/com/android/phone/PhoneUtils.java
@@ -288,6 +288,8 @@
                 // Check is phone in any dock, and turn on speaker accordingly
                 final boolean speakerActivated = activateSpeakerIfDocked(phone);
 
+                final BluetoothManager btManager = app.getBluetoothManager();
+
                 // When answering a phone call, the user will move the phone near to her/his ear
                 // and start conversation, without checking its speaker status. If some other
                 // application turned on the speaker mode before the call and didn't turn it off,
@@ -297,7 +299,7 @@
                 // - we did not activate speaker by ourselves during the process above, and
                 // - Bluetooth headset is not in use.
                 if (isRealIncomingCall && !speakerActivated && isSpeakerOn(app)
-                        && !app.isBluetoothHeadsetAudioOn()) {
+                        && !btManager.isBluetoothHeadsetAudioOn()) {
                     // This is not an error but might cause users' confusion. Add log just in case.
                     Log.i(LOG_TAG, "Forcing speaker off due to new incoming call...");
                     turnOnSpeaker(app, false, true);
@@ -711,9 +713,11 @@
             // Check is phone in any dock, and turn on speaker accordingly
             final boolean speakerActivated = activateSpeakerIfDocked(phone);
 
+            final BluetoothManager btManager = app.getBluetoothManager();
+
             // See also similar logic in answerCall().
             if (initiallyIdle && !speakerActivated && isSpeakerOn(app)
-                    && !app.isBluetoothHeadsetAudioOn()) {
+                    && !btManager.isBluetoothHeadsetAudioOn()) {
                 // This is not an error but might cause users' confusion. Add log just in case.
                 Log.i(LOG_TAG, "Forcing speaker off when initiating a new outgoing call...");
                 PhoneUtils.turnOnSpeaker(app, false, true);
@@ -2457,8 +2461,9 @@
         if (PhoneGlobals.mDockState != Intent.EXTRA_DOCK_STATE_UNDOCKED) {
             if (DBG) log("activateSpeakerIfDocked(): In a dock -> may need to turn on speaker.");
             PhoneGlobals app = PhoneGlobals.getInstance();
+            final BluetoothManager btManager = app.getBluetoothManager();
 
-            if (!app.isHeadsetPlugged() && !app.isBluetoothHeadsetAudioOn()) {
+            if (!app.isHeadsetPlugged() && !btManager.isBluetoothHeadsetAudioOn()) {
                 turnOnSpeaker(phone.getContext(), true, true);
                 activated = true;
             }
diff --git a/src/com/android/phone/Ringer.java b/src/com/android/phone/Ringer.java
index a882490..a0096ea 100644
--- a/src/com/android/phone/Ringer.java
+++ b/src/com/android/phone/Ringer.java
@@ -55,6 +55,7 @@
     // Uri for the ringtone.
     Uri mCustomRingtoneUri = Settings.System.DEFAULT_RINGTONE_URI;
 
+    private final BluetoothManager mBluetoothManager;
     Ringtone mRingtone;
     Vibrator mVibrator;
     IPowerManager mPowerManager;
@@ -70,10 +71,10 @@
      * Initialize the singleton Ringer instance.
      * This is only done once, at startup, from PhoneApp.onCreate().
      */
-    /* package */ static Ringer init(Context context) {
+    /* package */ static Ringer init(Context context, BluetoothManager bluetoothManager) {
         synchronized (Ringer.class) {
             if (sInstance == null) {
-                sInstance = new Ringer(context);
+                sInstance = new Ringer(context, bluetoothManager);
             } else {
                 Log.wtf(LOG_TAG, "init() called multiple times!  sInstance = " + sInstance);
             }
@@ -82,9 +83,11 @@
     }
 
     /** Private constructor; @see init() */
-    private Ringer(Context context) {
+    private Ringer(Context context, BluetoothManager bluetoothManager) {
         mContext = context;
-        mPowerManager = IPowerManager.Stub.asInterface(ServiceManager.getService(Context.POWER_SERVICE));
+        mBluetoothManager = bluetoothManager;
+        mPowerManager = IPowerManager.Stub.asInterface(
+                ServiceManager.getService(Context.POWER_SERVICE));
         // We don't rely on getSystemService(Context.VIBRATOR_SERVICE) to make sure this
         // vibrator object will be isolated from others.
         mVibrator = new SystemVibrator(context);
@@ -149,7 +152,7 @@
 
         synchronized (this) {
             try {
-                if (PhoneGlobals.getInstance().showBluetoothIndication()) {
+                if (mBluetoothManager.showBluetoothIndication()) {
                     mPowerManager.setAttentionLight(true, 0x000000ff);
                 } else {
                     mPowerManager.setAttentionLight(true, 0x00ffffff);