说明

突然有一天, 我发现了linphone的蓝牙耳机功能, 但是由于功能进度的需要,打算将蓝牙去掉.

步骤

1. 线去掉核心.

主要是BluetoothManager类, 后边详细贴出.

2. 去掉LinphoneManager中的Bluetooth处理

private void routeAudioToSpeakerHelper(boolean speakerOn) {
        Log.w("Routing audio to " + (speakerOn ? "speaker" : "earpiece") + ", disabling bluetooth audio route");
        BluetoothManager.getInstance().disableBluetoothSCO();
        if (checkmLcIsValide(mLc)) {
            mLc.enableSpeaker(speakerOn);
        }
    }
public static void BluetoothManagerDestroy() {
        if (BluetoothManager.getInstance() != null)
            BluetoothManager.getInstance().destroy();
    }
callState()

 else if (state == State.IncomingReceived || (state == State.CallIncomingEarlyMedia && mR.getBoolean(R.bool.allow_ringing_while_early_media))) {
            LinphoneUtils._log("LinphoneManager#config", "else if (state == State.IncomingReceived || (state == State.CallIncomingEarlyMedia && mR.getBoolean(R.bool.allow_ringing_while_early_media)))");
            // Brighten screen for at least 10 seconds
            if (mLc.getCallsNb() == 1) {
                requestAudioFocus(STREAM_RING);
                BluetoothManager.getInstance().disableBluetoothSCO(); // Just in case

                ringingCall = call;
                startRinging();
                // otherwise there is the beep
            }
        }
public void startBluetooth() {
        if (BluetoothManager.getInstance().isBluetoothHeadsetAvailable()) {
            BluetoothManager.getInstance().routeAudioToBluetooth();
            // Hack to ensure the bluetooth route is really used
            mHandler.postDelayed(new Runnable() {
                @Override
                public void run() {
                    BluetoothManager.getInstance().routeAudioToBluetooth();
                }
            }, 500);
        }
    }
// You may need to call galaxys audio hack after this method
        if (!BluetoothManager.getInstance().isBluetoothHeadsetAvailable()) {
            if (mServiceContext.getResources().getBoolean(R.bool.isTablet)) {
                Log.d("Stopped ringing, routing back to speaker");
                routeAudioToSpeaker();
            } else {
                Log.d("Stopped ringing, routing back to earpiece");
                routeAudioToReceiver();
            }
        }

3. CallActivity.java中的Bluetooth应用

onCreate
        if (!BluetoothManager.getInstance().isBluetoothHeadsetAvailable()) {
            BluetoothManager.getInstance().initBluetooth();
        }
callstate
                            if (BluetoothManager.getInstance().isBluetoothHeadsetAvailable()) {
                BluetoothManager.getInstance().routeAudioToBluetooth();
            }
initUI
        if (false/*BluetoothManager.getInstance().isBluetoothHeadsetAvailable()*/) {
            try {
                audioRoute.setVisibility(View.VISIBLE);
                speaker.setVisibility(View.GONE);
            } catch (NullPointerException npe) {
                Log.e("Bluetooth: Audio routes menu disabled on tablets for now (2)");
            }
        } else {
            try {
                audioRoute.setVisibility(View.GONE);
                speaker.setVisibility(View.VISIBLE);
            } catch (NullPointerException npe) {
                Log.e("Bluetooth: Audio routes menu disabled on tablets for now (3)");
            }
        }
refreshInCallActions
            if (BluetoothManager.getInstance().isUsingBluetoothAudioRoute()) {
                routeEarpiece.setImageResource(R.drawable.route_earpiece);
                routeBluetooth.setImageResource(R.drawable.route_bluetooth_selected);
            } else {
                routeEarpiece.setImageResource(R.drawable.route_earpiece_selected);
                routeBluetooth.setImageResource(R.drawable.route_bluetooth);
            }
onClick
else if (id == R.id.route_bluetooth) {
            if (BluetoothManager.getInstance().routeAudioToBluetooth()) {
                isSpeakerEnabled = false;
                routeBluetooth.setImageResource(R.drawable.route_bluetooth_selected);
                routeSpeaker.setImageResource(R.drawable.route_speaker);
                routeEarpiece.setImageResource(R.drawable.route_earpiece);
            }
            hideOrDisplayAudioRoutes();
showVideoView
        if (!BluetoothManager.getInstance().isBluetoothHeadsetAvailable()) {
            Log.w("Bluetooth not available, using speaker");
            LinphoneManager.getInstance().routeAudioToSpeaker();
            isSpeakerEnabled = true;
        }

4. LinphoneLauncherActivty中Bluetooth的应用

onCreate
            if (Version.sdkAboveOrEqual(Version.API11_HONEYCOMB_30)) {
                BluetoothManager.getInstance().initBluetooth();
            }


 // We need LinphoneService to start bluetoothManager
                            if (Version.sdkAboveOrEqual(Version.API11_HONEYCOMB_30)) {
                                BluetoothManager.getInstance().initBluetooth();
                            }
stopLinphoneService
if (Version.sdkAboveOrEqual(Version.API11_HONEYCOMB_30)) {
            BluetoothManager.getInstance().destroy();
        }

5. LinphoneService中Bluetooth的应用

startLinphoneService()
                    handler.post(new Runnable() {
                        @Override
                        public void run() {
                            //WE need LinphoneService to start bluetoothManager
                            if (Version.sdkAboveOrEqual(Version.API11_HONEYCOMB_30)) {
                                BluetoothManager.getInstance().initBluetooth();
                            }
                        }
                    });

超长代码

BluetoothManager.java

package org.linphone;
/*
BluetoothManager.java
Copyright (C) 2012  Belledonne Communications, Grenoble, France

This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
*/

import java.util.List;

import org.linphone.mediastream.Log;

import android.annotation.TargetApi;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothAssignedNumbers;
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.media.AudioManager;
import android.os.Build;

/**
 * @author Sylvain Berfini
 */
@TargetApi(Build.VERSION_CODES.HONEYCOMB)
public class BluetoothManager extends BroadcastReceiver {
    public int PLANTRONICS_BUTTON_PRESS = 1;
    public int PLANTRONICS_BUTTON_LONG_PRESS = 2;
    public int PLANTRONICS_BUTTON_DOUBLE_PRESS = 5;

    public int PLANTRONICS_BUTTON_CALL = 2;
    public int PLANTRONICS_BUTTON_MUTE = 3;

    private static BluetoothManager instance;

    private Context mContext;
    private AudioManager mAudioManager;
    private BluetoothAdapter mBluetoothAdapter;
    private BluetoothHeadset mBluetoothHeadset;
    private BluetoothDevice mBluetoothDevice;
    private BluetoothProfile.ServiceListener mProfileListener;
    private boolean isBluetoothConnected;
    private boolean isScoConnected;

    public static BluetoothManager getInstance() {
        if (instance == null) {
            instance = new BluetoothManager();
        }
        return instance;
    }

    public BluetoothManager() {
        isBluetoothConnected = false;
        if (!ensureInit()) {
            //Log.w("[Bluetooth] Manager tried to init but LinphoneService not ready yet...");
        }
        instance = this;
    }

    public void initBluetooth() {
        if (!ensureInit()) {
            Log._w("[Bluetooth] Manager tried to init bluetooth but LinphoneService not ready yet...");
            return;
        }

        IntentFilter filter = new IntentFilter();
        filter.addCategory(BluetoothHeadset.VENDOR_SPECIFIC_HEADSET_EVENT_COMPANY_ID_CATEGORY + "." + BluetoothAssignedNumbers.PLANTRONICS);
        filter.addAction(AudioManager.ACTION_SCO_AUDIO_STATE_UPDATED);
        filter.addAction(BluetoothAdapter.ACTION_CONNECTION_STATE_CHANGED);
        filter.addAction(BluetoothHeadset.ACTION_VENDOR_SPECIFIC_HEADSET_EVENT);
        mContext.registerReceiver(this, filter);
        Log._d("[Bluetooth] Receiver started");

        startBluetooth();
    }

    private void startBluetooth() {
        if (isBluetoothConnected) {
            Log._e("[Bluetooth] Already started, skipping...");
            return;
        }

        mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();

        if (mBluetoothAdapter != null && mBluetoothAdapter.isEnabled()) {
            if (mProfileListener != null) {
                Log._w("[Bluetooth] Headset profile was already opened, let's close it");
                mBluetoothAdapter.closeProfileProxy(BluetoothProfile.HEADSET, mBluetoothHeadset);
            }

            mProfileListener = new BluetoothProfile.ServiceListener() {
                public void onServiceConnected(int profile, BluetoothProfile proxy) {
                    if (profile == BluetoothProfile.HEADSET) {
                        Log._d("[Bluetooth] Headset connected");
                        mBluetoothHeadset = (BluetoothHeadset) proxy;
                        isBluetoothConnected = true;
                    }
                }

                public void onServiceDisconnected(int profile) {
                    if (profile == BluetoothProfile.HEADSET) {
                        mBluetoothHeadset = null;
                        isBluetoothConnected = false;
                        Log._d("[Bluetooth] Headset disconnected");
                        LinphoneManager.getInstance().routeAudioToReceiver();
                    }
                }
            };
            boolean success = mBluetoothAdapter.getProfileProxy(mContext, mProfileListener, BluetoothProfile.HEADSET);
            if (!success) {
                Log._e("[Bluetooth] getProfileProxy failed !");
            }
        } else {
            Log._w("[Bluetooth] Interface disabled on device");
        }
    }

    private boolean ensureInit() {
        if (mBluetoothAdapter == null) {
            mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
        }
        if (mContext == null) {
            if (LinphoneService.isReady()) {
                mContext = LinphoneService.instance().getApplicationContext();
            } else {
                return false;
            }
        }
        if (mContext != null && mAudioManager == null) {
            mAudioManager = ((AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE));
        }
        return true;
    }

    public boolean routeAudioToBluetooth() {
        ensureInit();

        if (mBluetoothAdapter != null && mBluetoothAdapter.isEnabled() && mAudioManager != null && mAudioManager.isBluetoothScoAvailableOffCall()) {
            if (isBluetoothHeadsetAvailable()) {
                if (mAudioManager != null && !mAudioManager.isBluetoothScoOn()) {
                    Log._d("[Bluetooth] SCO off, let's start it");
                    mAudioManager.setBluetoothScoOn(true);
                    mAudioManager.startBluetoothSco();
                }
            } else {
                return false;
            }

            // Hack to ensure bluetooth sco is really running
            boolean ok = isUsingBluetoothAudioRoute();
            int retries = 0;
            while (!ok && retries < 5) {
                retries++;

                try {
                    Thread.sleep(200);
                } catch (InterruptedException e) {
                }

                if (mAudioManager != null) {
                    mAudioManager.setBluetoothScoOn(true);
                    mAudioManager.startBluetoothSco();
                }

                ok = isUsingBluetoothAudioRoute();
            }
            if (ok) {
                if (retries > 0) {
                    Log._d("[Bluetooth] Audio route ok after " + retries + " retries");
                } else {
                    Log._d("[Bluetooth] Audio route ok");
                }
            } else {
                Log._d("[Bluetooth] Audio route still not ok...");
            }

            return ok;
        }

        return false;
    }

    public boolean isUsingBluetoothAudioRoute() {
        return mBluetoothHeadset != null && mBluetoothHeadset.isAudioConnected(mBluetoothDevice) && isScoConnected;
    }

    public boolean isBluetoothHeadsetAvailable() {
        ensureInit();
        if (mBluetoothAdapter != null && mBluetoothAdapter.isEnabled() && mAudioManager != null && mAudioManager.isBluetoothScoAvailableOffCall()) {
            boolean isHeadsetConnected = false;
            if (mBluetoothHeadset != null) {
                List<BluetoothDevice> devices = mBluetoothHeadset.getConnectedDevices();
                mBluetoothDevice = null;
                for (final BluetoothDevice dev : devices) {
                    if (mBluetoothHeadset.getConnectionState(dev) == BluetoothHeadset.STATE_CONNECTED) {
                        mBluetoothDevice = dev;
                        isHeadsetConnected = true;
                        break;
                    }
                }
                Log._d(isHeadsetConnected ? "[Bluetooth] Headset found, bluetooth audio route available" : "[Bluetooth] No headset found, bluetooth audio route unavailable");
            }
            return isHeadsetConnected;
        }

        return false;
    }

    public void disableBluetoothSCO() {
        if (mAudioManager != null && mAudioManager.isBluetoothScoOn()) {
            mAudioManager.stopBluetoothSco();
            mAudioManager.setBluetoothScoOn(false);

            // Hack to ensure bluetooth sco is really stopped
            int retries = 0;
            while (isScoConnected && retries < 10) {
                retries++;

                try {
                    Thread.sleep(200);
                } catch (InterruptedException e) {
                }

                mAudioManager.stopBluetoothSco();
                mAudioManager.setBluetoothScoOn(false);
            }
            Log._w("[Bluetooth] SCO disconnected!");
        }
    }

    public void stopBluetooth() {
        Log._w("[Bluetooth] Stopping...");
        isBluetoothConnected = false;

        disableBluetoothSCO();

        if (mBluetoothAdapter != null && mProfileListener != null && mBluetoothHeadset != null) {
            mBluetoothAdapter.closeProfileProxy(BluetoothProfile.HEADSET, mBluetoothHeadset);
            mProfileListener = null;
        }
        mBluetoothDevice = null;

        Log._w("[Bluetooth] Stopped!");

        if (LinphoneManager.isInstanciated()) {
            LinphoneManager.getInstance().routeAudioToReceiver();
        }
    }

    public void destroy() {
        try {
            stopBluetooth();

            try {
                mContext.unregisterReceiver(this);
                Log._d("[Bluetooth] Receiver stopped");
            } catch (Exception e) {
            }
        } catch (Exception e) {
            Log._e(e);
        }
    }

    public void onReceive(Context context, Intent intent) {
        if (!LinphoneManager.isInstanciated())
            return;

        String action = intent.getAction();
        if (AudioManager.ACTION_SCO_AUDIO_STATE_UPDATED.equals(action)) {
            int state = intent.getIntExtra(AudioManager.EXTRA_SCO_AUDIO_STATE, 0);
            if (state == AudioManager.SCO_AUDIO_STATE_CONNECTED) {
                Log._d("[Bluetooth] SCO state: connected");
//              LinphoneManager.getInstance().audioStateChanged(AudioState.BLUETOOTH);
                isScoConnected = true;
            } else if (state == AudioManager.SCO_AUDIO_STATE_DISCONNECTED) {
                Log._d("[Bluetooth] SCO state: disconnected");
//              LinphoneManager.getInstance().audioStateChanged(AudioState.SPEAKER);
                isScoConnected = false;
            } else {
                Log._d("[Bluetooth] SCO state: " + state);
            }
        } else if (BluetoothAdapter.ACTION_CONNECTION_STATE_CHANGED.equals(action)) {
            int state = intent.getIntExtra(BluetoothAdapter.EXTRA_CONNECTION_STATE, BluetoothAdapter.STATE_DISCONNECTED);
            if (state == 0) {
                Log._d("[Bluetooth] State: disconnected");
                stopBluetooth();
            } else if (state == 2) {
                Log._d("[Bluetooth] State: connected");
                startBluetooth();
            } else {
                Log._d("[Bluetooth] State: " + state);
            }
        } else if (intent.getAction().equals(BluetoothHeadset.ACTION_VENDOR_SPECIFIC_HEADSET_EVENT)) {
            String command = intent.getExtras().getString(BluetoothHeadset.EXTRA_VENDOR_SPECIFIC_HEADSET_EVENT_CMD);
            //int type = intent.getExtras().getInt(BluetoothHeadset.EXTRA_VENDOR_SPECIFIC_HEADSET_EVENT_CMD_TYPE);

            Object[] args = (Object[]) intent.getExtras().get(BluetoothHeadset.EXTRA_VENDOR_SPECIFIC_HEADSET_EVENT_ARGS);
            String eventName = (String) args[0];

            if (eventName.equals("BUTTON") && args.length >= 3) {
                Integer buttonID = (Integer) args[1];
                Integer mode = (Integer) args[2];
                Log._d("[Bluetooth] Event: " + command + " : " + eventName + ", id = " + buttonID + " (" + mode + ")");
            }
        }
    }
}