android 4.0 bluetooth bt HFP/HSP分析

来源:互联网 发布:mac 地址 库 编辑:程序博客网 时间:2024/06/06 12:29

1.1          路径

 Package/apps/Phone

BluetoothHandsfree.java

BluetoothHeadsetService.java

BluetoothAtPhonebook.java

BluetoothCmeError.java

Ø  Package/apps/settings/Bluetooth

HeadsetProfile.java

Ø  Framework/base/core/java/android/Bluetooth

HeadsetBase.java

BluetoothHeadset.java

1.2         流程

1.2.1    启动蓝牙服务

Android 实现了对HeadsetHandsfree两种profile的支持。其实现核心是BluetoothHeadsetService,在PhoneApp创建的时候会通过startService()启动它。

 

电话应用启动时(PhoneApponCreate里),就会启动HSP服务

         if (BluetoothAdapter.getDefaultAdapter() != null) {

                // Start BluetoothHandsree even if device is not voice capable.

                mBtHandsfree = BluetoothHandsfree.init(this, mCM);

                startService(new Intent(this, BluetoothHeadsetService.class));

           }

 

1.2.2    连接和切换蓝牙通道

BluetoothHeadsetServiceonStart里,判断蓝牙是否可用(Return true if Bluetooth is currently enabled and ready for use),然后就切换到蓝牙通道 

 if (mAdapter.isEnabled()) {

mAg.start(mIncomingConnectionHandler);

      mBtHandsfree.onBluetoothEnabled();

          }

if 语句里共有两句话,两句话的作用分别是:

第一句:连接

第二句:切换到蓝牙通道

BluetoothHeadsetService启动后即会进行HSP/HFP的判断,并且会接收不同的广播,来连接和切换通道,断开连接和断开蓝牙通道

if (action.equals(BluetoothAdapter.ACTION_STATE_CHANGED)) {

                switch (intent.getIntExtra(BluetoothAdapter.EXTRA_STATE,BluetoothAdapter.ERROR)) {

                caseBluetoothAdapter.STATE_ON:

                           mAg.start(mIncomingConnectionHandler);

                      mBtHandsfree.onBluetoothEnabled();

                break;

                caseBluetoothAdapter.STATE_TURNING_OFF:

                    mBtHandsfree.onBluetoothDisabled();

                    mAg.stop();

                    if (currDevice != null) {

                        setState(currDevice, BluetoothHeadset.STATE_DISCONNECTED);

                    }

}

 

1.2.2.1         连接

第一句是一个BluetoothAudioGateway mAg,在它的start里,检查headset, hands free的通道连接状况,并通过Handler发出MSG_INCOMING_HANDSFREE_CONNECTION   MSG_INCOMING_HEADSET_CONNECTION的消息,之后的处理notifyIncomingConnection,使远端蓝牙设备连接进来。

notifyIncomingConnection调用到BluetoothService里,并发送了一个消息,这个消息发送到了BluetoothDeviceProfileState里,在这里面实现了蓝牙的连接且这里的连接方式同A2DP的连接是类似的,在BluetoothDeviceProfileState里实现mHeadsetService.connectHeadsetInternal()

1.2.2.2         连接调用流程

BluetoothService   notifyIncomingConnection ()   ---->

BluetoothDeviceProfileState(  mHeadsetService.connectHeadsetInternal()   )

这里面通过状态机,一系列复杂的流程之后,最终调用的是

BluetoothHeadsetServiceconnectHeadsetInternal())

1.2.2.3         切换蓝牙通道

onBluetoothEnabled 有两处调用,除了BluetoothHeadsetServiceonstart(),mBluetoothReceiver也实现了切换蓝牙通道,我们看下另一处mBluetoothReceiver,这里是当接收到BluetoothAdapter.ACTION_STATE_CHANGED广播,状态是BluetoothAdapter.STATE_ON的状态时,也会切换到蓝牙通道。

切换蓝牙通道时,会有对A2DP的开和关的控制,调用的是audioOn()和 audioOff()

最终调用的是BluetoothA2dp里的suspendSink()和 resumeSink()。

audioOn()时,会发送一个消息MESSAGE_CHECK_PENDING_SCOHandsfreeMessageHandler,它的处理如下:

3  audioOn

 

这里有个connectScoThread(),我们一步步跟下去会发现它其它建立了一个SCO连接。

连接成功后会调用setBluetoothScoOn(),切换到蓝牙通道。

4  connectSco

 

 

 

 

 

1.2.2.4         切换蓝牙通道调用流程

onBluetoothEnabled()---->

IncomingScoAcceptThread.java---->

connectSco()---->

setBluetoothScoOn(true)

 

Ø  frameworks\base\core\java\android\bluetooth

IBluetoothA2dp.aidl

BluetoothA2dp.java

Ø  packages\apps\Settings\src\com\android\settings\bluetooth

A2dpProfile.java

Ø  frameworks\base\core\java\android\server

BluetoothA2dpService.java

Ø  frameworks\base\core\jni

android_server_ BluetoothA2dpService.cpp

2.3          流程

2.3.1    A2DP连接调用过程

BluetoothDevicePreference(    onClick() / connect()   )配对

CachedBluetoothDevice.connect()---->

CachedBluetoothDevice. connectWithoutResettingTimer()---->

connectInt()---->

profile.connect(mDevice)

这时的profileLocalBluetoothProfile,由其子类A2DProfile实现connect方法

A2dpProfile(   connect()   ) ---->

BluetoothA2DP(   connect()   ) ---->

BluetoothA2DPService(  connect()  ) ---->

BluetoothService(   connectSink()   ) ---->

BluetoothProfileState   dispatchMessage() ---->

BluetoothDeviceProfileState(  mA2dpService.connectSinkInternal(mDevice)    )

这里面通个状态机,一系列复杂的流程之后,最终调用的是

BluetoothA2dpService  connectSinkInternal())

2.3.2    connectSink分析

这里的状态,简要描述一下:

BluetoothService.java

Code:

        public boolean connectSink(String address) {

        if (getBondState(address) != BluetoothDevice.BOND_BONDED) return false;

           BluetoothDeviceProfileState state = mDeviceProfileState.get(address);

                 if (state != null) {

                           Message msg = new Message();

                          msg.arg1 = BluetoothDeviceProfileState.CONNECT_A2DP_OUTGOING;

                            msg.obj = state;

                             mA2dpProfileState.sendMessage(msg);

                            return true;

                   }

         return false;

}

 

这里主要是发送了一个消息BluetoothDeviceProfileState.CONNECT_A2DP_OUTGOING

这个消息的处理是在BluetoothProfileState.java里,进入BluetoothProfileState后,发现跟BluetoothAdapterStateMachine一样,也是用到了状态机的模式,那么,是在哪个状态里处理了这个消息呢?我们看它的构造函数,

 

        addState(mStableState);

        addState(mPendingCommandState);

        setInitialState(mStableState);

 

说明它的初始状态是mStableState,由此进一步分,它会走到StableState.java里的Enter(),然后走dispatchMessage(),而在dispatchMessage()

 

private void dispatchMessage(Message msg) {

BluetoothDeviceProfileState deviceProfileMgr = (BluetoothDeviceProfileState)msg.obj;

           int cmd = msg.arg1;

       if (mPendingDevice == null || mPendingDevice.equals(deviceProfileMgr.getDevice())) {

                  mPendingDevice = deviceProfileMgr.getDevice();

                  deviceProfileMgr.sendMessage(cmd);

              } else {

                       Message deferMsg = new Message();

                  deferMsg.arg1 = cmd;

                  deferMsg.obj = deviceProfileMgr;

                  deferMessage(deferMsg);

                          }

           }

   

 

最终又通过deferMessage,调用到BluetoothDeviceProfileState里去处理这个消息,上面绕了这么大一个圈子,只说明了一件事,CONNECT_A2DP_OUTGOING,这个消息,经过层层封装,最张交由BluetoothDeviceProfileState去处理,最终在processCommand方法,处理了这个消息

 

case CONNECT_A2DP_OUTGOING:

                if (mA2dpService != null) {

                            return mA2dpService.connectSinkInternal(mDevice);

                }

 break;

 

可以看到,connectSink()实现调用的是connectSinkInternal()方法,从而进一步经JNI,调到Bluez里。

 

 

 

原创粉丝点击