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 实现了对Headset和Handsfree 两种profile的支持。其实现核心是BluetoothHeadsetService,在PhoneApp创建的时候会通过startService()启动它。

电话应用启动时(PhoneApp的onCreate里),就会启动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    连接和切换蓝牙通道

BluetoothHeadsetService的onStart里,判断蓝牙是否可用(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()   )

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

BluetoothHeadsetService(connectHeadsetInternal())

1.2.2.3         切换蓝牙通道

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

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

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

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

图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)

这时的profile是LocalBluetoothProfile ,由其子类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里。

android 4.0 bluetooth bt HFP/HSP分析相关推荐

  1. 【Android 安全】DEX 加密 ( 不同 Android 版本的 DEX 加载 | Android 8.0 版本 DEX 加载分析 | Android 5.0 版本 DEX 加载分析 )

    文章目录 一.不同版本的 DEX 加载 1.Android 8.0 版本 DEX 加载分析 2.Android 6.0 版本 DEX 加载分析 3.Android 5.0 版本 DEX 加载分析 一. ...

  2. Android 7.0 挂断电话流程分析

    1.图形显示 挂断电话分为本地挂断和远程对方挂断 2.本地挂断 1).点击按钮 先看按键的监听事件 CallCardFragment.java 中有对按钮的监听事件 @Overridepublic v ...

  3. android 蓝牙扫描流程,Android 9.0 Bluetooth源码分析(二)蓝牙扫描流程

    1 UI 蓝牙开始扫描位于setting的 /packages/apps/Settings/src/com/android/settings/bluetooth/BluetoothPairingDet ...

  4. Android 7.0 Gallery图库源码分析2 - 分析启动流程

    前面一讲解了Gallery启动Activity以及界面如何绘制,现在开始讲解启动流程的代码逻辑.  GalleryActivity的onCreate方法中调用initializeByIntent()方 ...

  5. Android 7.0 APN 拨号上网流程分析

    1.前言 在前段时间的项目中遇到客户的设备出现APN断开的情况没有自动连接,后来折腾了一段时间解决了这个问题.现在用这篇博客记录一下APN的选择和连接流程. 2.名词解析 APN:APN指一种网络接入 ...

  6. Android 9.0 SystemUI 锁屏流程分析

    1.锁屏界面显示的流程 2.按键灭屏 -> 按键亮屏 对于Key事件,InputDispatcher在分发之前会先将事件上发到PhoneWindowManager中,可以进行拦截,故从Phone ...

  7. Android 9.0 开关机动画流程分析

    Android开机动画流程的启动主要是在Surfaseflinger里面完成的,具体代码如下: /frameworks/native/services/surfaceflinger/StartProp ...

  8. Android 4.0 Launcher源码详细分析 傻蛋

    http://wenku.baidu.com/view/80e280e2998fcc22bcd10dcd.html

  9. android 4.0的手机,酷派推出国内首款Android 4.0手机

    文/DIY 在谷歌联手三星发布Android 4.0之后,小米科技相关负责人25日在媒体上表态,声称要在"国内第一个上4.0".对此,业界反应不一.援引北青网观点认为," ...

最新文章

  1. 安装phpMyAdmin图文教程
  2. linux shell 脚本练习,shell脚本小练习
  3. 几句话就能让你明白:IPv6与Voip
  4. 数据丢包怎么修复_交换机发生网络通信故障怎么解决?
  5. 724 Find Pivot Index
  6. 机器学习篇-指标:AUC
  7. java exception 二次抛出_java – 如何在scheduleWithFixedDelay抛出异常时重新启动计划?...
  8. 电子表格转换成数据库_创建数据库,将电子表格转换为关系数据库,第1部分...
  9. 活动丨PGConf.Asia大会11月17-20日线上直播!
  10. Android 系统(179)--- .ko 加载失败
  11. php 辅助函数,辅助函数 - Laravel 5.8 中文文档手册 - php中文网手册
  12. 【BZOJ】1002: [FJOI2007]轮状病毒 递推+高精度
  13. Dropdownlist插入值!
  14. 【error】RuntimeError: size mismatch
  15. day 9.1 逻辑回归-二元回归与多元回归
  16. 外包公司的三大弊端是什么,在此情况下还建议去外包公司吗
  17. [Unity3D]Unity3D游戏开发之鼠标旋转、缩放实现3D物品展示
  18. Hypervisor 简介
  19. html中repeat的作用,深入探究CSS repeat()函数知识及用法
  20. 自动白平衡技术(WhiteBalance)(转自Harri的blog)

热门文章

  1. Modelsim SE-64 10.4建立UVM环境
  2. 【C语言】之实现闰年判断
  3. 正负用c语言表示,用C表示负数?
  4. 前端页面项目——博客系统
  5. 计算机基础应用期末卷,计算机应用基础期末试卷(B卷)
  6. win 10 QT 5.15.2 modbus QModbusRtuSerialMaster 客户端
  7. linux下网卡参数配置,linux网卡配置参数
  8. jmeter+csv+ant接口自动化测试--设计jmeter脚本(一)
  9. IntelliJ IDEA像Eclipse一样打开工作空间,管理多个项目
  10. 大龄程序员的前途在哪里?