4.3 android bluetooth hfp分析

2013-08-20 20:16 592人阅读 评论(3) 收藏 举报

所有程序执行的代码都是有入口的,在这里我们暂时分析一种情景,蓝牙打开着,蓝牙耳机连接。

在设置界面点击蓝牙耳机操作:

packages/apps/Settings/src/com/android/settings/bluetooth/BluetoothDevicePreference.java

  1. void onClicked() {
  2. int bondState = mCachedDevice.getBondState();
  3. if (mCachedDevice.isConnected()) {
  4. askDisconnect();
  5. } else if (bondState == BluetoothDevice.BOND_BONDED) { //已经配对,但是未连接
  6. <span style="color:#ff0000;">mCachedDevice.connect(true);</span>
  7. } else if (bondState == BluetoothDevice.BOND_NONE) { //没有配对
  8. <span style="color:#ff0000;">pair();</span>
  9. }
  10. }

mCachedDevice.connect(true);方法会直接调用CachedBluetoothDevice.java的connect的方法。

packages/apps/Settings/src/com/android/settings/bluetooth/CachedBluetoothDevice.java

  1. void connect(boolean connectAllProfiles) {
  2. if (!ensurePaired()) {  //配对处理暂时不关注
  3. return;
  4. }
  5. mConnectAttempted = SystemClock.elapsedRealtime();
  6. connectWithoutResettingTimer(connectAllProfiles);
  7. }

代码执行到connectWithoutResettingTimer,注释就不粘贴了。

  1. private void connectWithoutResettingTimer(boolean connectAllProfiles) {
  2. ......
  3. // Reset the only-show-one-error-dialog tracking variable
  4. mIsConnectingErrorPossible = true;
  5. int preferredProfiles = 0;
  6. for (LocalBluetoothProfile profile : mProfiles) {
  7. if (connectAllProfiles ? profile.isConnectable() : profile.isAutoConnectable()) {
  8. if (profile.isPreferred(mDevice)) {
  9. ++preferredProfiles;
  10. connectInt(profile);
  11. }
  12. }
  13. }
  14. if (DEBUG) Log.d(TAG, "Preferred profiles = " + preferredProfiles);
  15. if (preferredProfiles == 0) {
  16. connectAutoConnectableProfiles();
  17. }
  18. }

不同的协议实现类,继承于LocalBluetoothProfile,以HeadsetProfile为例。

  1. synchronized void connectInt(LocalBluetoothProfile profile) {
  2. if (!ensurePaired()) {
  3. return;
  4. }
  5. if (<span style="color:#ff0000;">profile.connect(mDevice)</span>) {
  6. if (Utils.D) {
  7. Log.d(TAG, "Command sent successfully:CONNECT " + describe(profile));
  8. }
  9. return;
  10. }
  11. Log.i(TAG, "Failed to connect " + profile.toString() + " to " + mName);
  12. }

这个方法中,profile.connnect(),直接调用 HeadsetProfile的connnect(),其实headset/handsfree 是共用同一个service。

packages/apps/Settings/src/com/android/settings/bluetooth/HeadsetProfile.java

  1. public boolean connect(BluetoothDevice device) {
  2. if (mService == null) return false;
  3. List<BluetoothDevice> sinks = mService.getConnectedDevices();
  4. if (sinks != null) {//断开所有连接
  5. for (BluetoothDevice sink : sinks) {
  6. mService.disconnect(sink);
  7. }
  8. }
  9. return mService.connect(device);
  10. }

mService.connect 直接进入framwork层,调用Bluetooth Headset server的api。

frameworks/base/core/java/android/bluetooth/BluetoothHeadset.java

  1. public boolean connect(BluetoothDevice device) {
  2. if (DBG) log("connect(" + device + ")");
  3. if (mService != null && isEnabled() &&
  4. isValidDevice(device)) {
  5. try {
  6. return mService.connect(device);
  7. } catch (RemoteException e) {
  8. Log.e(TAG, Log.getStackTraceString(new Throwable()));
  9. return false;
  10. }
  11. }
  12. if (mService == null) Log.w(TAG, "Proxy not attached to service");
  13. return false;
  14. }

framework 中转了一下,利用Binder机制再次进入Bluetooth模块。

frameworks/base/core/java/android/bluetooth/IBluetoothHeadset.aidl,实现类是

packages/apps/Bluetooth/src/com/android/bluetooth/hfp/HeadsetService.java
注意顺序:Setting->framewrok->Bluetooth

  1. public boolean connect(BluetoothDevice device) {
  2. HeadsetService service = getService();
  3. if (service == null) return false;
  4. return service.connect(device);
  5. }

IBluetoothHeadset.Sub仅仅是一个连接,什么都不做直接进入HeadsetService.java的connect 方法。

  1. public boolean connect(BluetoothDevice device) {
  2. enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
  3. "Need BLUETOOTH ADMIN permission");
  4. if (getPriority(device) == BluetoothProfile.PRIORITY_OFF) {
  5. return false;
  6. }
  7. int connectionState = mStateMachine.getConnectionState(device);
  8. if (connectionState == BluetoothProfile.STATE_CONNECTED ||
  9. connectionState == BluetoothProfile.STATE_CONNECTING) {
  10. return false;
  11. }
  12. mStateMachine.sendMessage(HeadsetStateMachine.CONNECT, device);//万恶的状态机开始工作
  13. return true;
  14. }

很讨厌android状态机,感觉把java搞成了非面向对象,进入状态机代码实现:

packages/apps/Bluetooth/src/com/android/bluetooth/hfp/HeadsetStateMachine.java

记住当前为 未连接状态,所以应该进入 Disconnected的状态,Disconnected的processMessage方法中:

  1. switch(message.what) {
  2. case CONNECT:
  3. BluetoothDevice device = (BluetoothDevice) message.obj;
  4. broadcastConnectionState(device, BluetoothProfile.STATE_CONNECTING,
  5. BluetoothProfile.STATE_DISCONNECTED);
  6. if (!<span style="color:#ff0000;">connectHfpNative(getByteAddress(device)) </span>) {
  7. broadcastConnectionState(device, BluetoothProfile.STATE_DISCONNECTED,
  8. BluetoothProfile.STATE_CONNECTING);
  9. break;
  10. }
  11. synchronized (HeadsetStateMachine.this) {
  12. mTargetDevice = device;
  13. transitionTo(mPending);
  14. }

connectHfpNative 连接,jni 方法 简单了。

packages\apps\bluetooth\jni\Com_android_bluetooth_hfp.cpp

  1. {"connectHfpNative", "([B)Z", (void *) connectHfpNative},
  1. static jboolean connectHfpNative(JNIEnv *env, jobject object, jbyteArray address) {
  2. jbyte *addr;
  3. bt_status_t status;
  4. ALOGI("%s: sBluetoothHfpInterface: %p", __FUNCTION__, sBluetoothHfpInterface);
  5. if (!sBluetoothHfpInterface) return JNI_FALSE;
  6. addr = env->GetByteArrayElements(address, NULL);
  7. if (!addr) {
  8. jniThrowIOException(env, EINVAL);
  9. return JNI_FALSE;
  10. }
  11. <span style="color:#ff0000;">if ((status = sBluetoothHfpInterface->connect((bt_bdaddr_t *)addr)) != BT_STATUS_SUCCESS) {</span>
  12. ALOGE("Failed HF connection, status: %d", status);
  13. }
  14. env->ReleaseByteArrayElements(address, addr, 0);
  15. return (status == BT_STATUS_SUCCESS) ? JNI_TRUE : JNI_FALSE;

关键函数是sBluetoothHfpInterface->connect((bt_bdaddr_t *)addr), connect定义在 hardware/libhardware/include/hardware/bt_hf.h,

但是实现却在:external/bluetooth/bluedroid/btif/src/btif_hf.c 中实现

  1. #include <hardware/bt_hf.h>
  2. static bt_status_t connect( bt_bdaddr_t *bd_addr )
  3. {
  4. CHECK_BTHF_INIT();
  5. return btif_queue_connect(UUID_SERVCLASS_AG_HANDSFREE, bd_addr, connect_int);
  6. }

看定义,直接连接handsfree

#define UUID_SERVCLASS_AG_HANDSFREE             0X111F  /* Handsfree profile */

btif_queue_connect的定义在下面文件中定义

external/bluetooth/bluedroid/btif/src/btif_profile_queue.c

进入bt_status_t btif_queue_connect看定义:

  1. bt_status_t btif_queue_connect(uint16_t uuid, const bt_bdaddr_t *bda,
  2. btif_connect_cb_t *connect_cb)
  3. {
  4. connect_node_t node;
  5. memset(&node, 0, sizeof(connect_node_t));
  6. memcpy(&(node.bda), bda, sizeof(bt_bdaddr_t));
  7. node.uuid = uuid;
  8. node.p_cb = connect_cb;
  9. return btif_transfer_context(queue_int_handle_evt, BTIF_QUEUE_CONNECT_EVT,
  10. (char*)&node, sizeof(connect_node_t), NULL);
  11. }

btif_transfer_context定义在如下文件中

external/bluetooth/bluedroid/btif/src/btif_core.c

btif_transfer_context内容就不粘贴了,里面发送消息,调用

external/bluetooth/bluedroid/gki/common/gki_buffer.c

  1. void GKI_send_msg (UINT8 task_id, UINT8 mbox, void *msg)

GKI_send_msg发送一条GKI信息到BTA,GKI_send_msg有三个参数,第一个参数是线程id,也作为task id, 通过bta_sys_init获得,第二个参数是mailbox id,第三个是上一步封装好的p_msg

GKI_send_msg
首先对p_msg进一步封装成event,通过链表存到mailbox
id对应的任务队列中,调用external/bluetooth/bluedroid/gki/ulinux/gki_ulinux.c
:: GKI_send_event
进行发送。
GKI_send_event设置事件掩码
gki_cb.com.OSWaitEvt[task_id] |= event;, 
通过pthread_cond_signal(&gki_cb.os.thread_evt_cond[task_id]);通知另外线程。
这样,在另外一个等待线程函数中gki_ulinux.c :: GKI_wait可以返回了。

ZT 4.3 android bluetooth hfp分析的更多相关文章

  1. 【转】Android bluetooth介绍(三): 蓝牙扫描(scan)设备分析

    原文网址:http://blog.csdn.net/xubin341719/article/details/38584469 关键词:蓝牙blueZ  A2DP.SINK.sink_connect.s ...

  2. Android bluetooth介绍(四): a2dp connect流程分析

    关键词:蓝牙blueZ  A2DP.SINK.sink_connect.sink_disconnect.sink_suspend.sink_resume.sink_is_connected.sink_ ...

  3. Android bluetooth介绍(三): 蓝牙扫描(scan)设备分析

    关键词:蓝牙blueZ  A2DP.SINK.sink_connect.sink_disconnect.sink_suspend.sink_resume.sink_is_connected.sink_ ...

  4. Android源码分析(六)-----蓝牙Bluetooth源码目录分析

    一 :Bluetooth 的设置应用 packages\apps\Settings\src\com\android\settings\bluetooth* 蓝牙设置应用及设置参数,蓝牙状态,蓝牙设备等 ...

  5. ZT Android Debuggerd的分析及使用方法

    Android Debuggerd的分析及使用方法 分类: 移动开发 android framework 2012-12-28 12:00 983人阅读 评论(0) 收藏 举报 目录(?)[+] An ...

  6. android4.3 Bluetooth(le)分析之startLeScan分析

    BluetoothAdapter.java中有low enery(le)的一些方法,android提供了这些方法,但源码中并未找到这些方法的调用之处.本文档主要分析这类方法的执行流程,来了解下le到底 ...

  7. 【转】Android bluetooth介绍(二): android blueZ蓝牙代码架构及其uart 到rfcomm流程

    原文网址:http://blog.sina.com.cn/s/blog_602c72c50102uzoj.html 关键词:蓝牙blueZ  UART  HCI_UART H4  HCI  L2CAP ...

  8. Android BLE与终端通信(二)——Android Bluetooth基础科普以及搜索蓝牙设备显示列表

    Android BLE与终端通信(二)--Android Bluetooth基础搜索蓝牙设备显示列表 摘要 第一篇算是个热身,这一片开始来写些硬菜了,这篇就是实际和蓝牙打交道了,所以要用到真机调试哟, ...

  9. Android Bluetooth模块学习笔记

    一.蓝牙基础知识 1.蓝牙( Bluetooth )是一种无线技术标准,可实现固定设备.移动设备和楼宇个人域网之间的短距离数据交换.蓝牙基于设备低成本的收发器芯片,传输距离近.低功耗. 2.微波频段: ...

随机推荐

  1. js / jquery 获取和设置 FCK Editor 的值

    开发中遇到 通过 $("#content").val(); 或者 document.getElementById("content"); 并不能获取到 id 为 ...

  2. 一张图解决Struts2添加源码

    主要是选择的路径:F:/struts2/struts-2.3.31/src/core/src/main/java

  3. 问题集录--Java高级软件工程师面试考纲(转)

    如果要应聘高级开发工程师职务,仅仅懂得Java的基础知识是远远不够的,还必须懂得常用数据结构.算法.网络.操作系统等知识.因此本文不会讲解具体的技术,笔者综合自己应聘各大公司的经历,整理了一份大公司对 ...

  4. SQL SERVER 原来还可以这样玩 FOR XML PATH

    FOR XML PATH 有的人可能知道有的人可能不知道,其实它就是将查询结果集以XML形式展现,有了它我们可以简化我们的查询语句实现一些以前可能需要借助函数活存储过程来完成的工作.那么以一个实例为主 ...

  5. 360手机新品牌5月6日公布 周鸿祎席地而坐谈AK47

    今年年初,周鸿祎又做了一个艰难的决定,南下做手机!经过好一番折腾终于搞出点动静,奔驰S600L也卖了(炒作的味道很浓重),一款代号为AK47的产品被确认,就连邀请函也充分的体现了周鸿祎的老兵情节.最近 ...

  6. Spring-boot简单的理解

    SpringBoot启动 SpringApplication.run(MyBootApplication.class); SpringApplication.run启动SpringBoot应用,主要过 ...

  7. [日常] Go语言圣经-WEB服务与习题

    Go语言圣经-web服务 1.Web服务程序,标准库里的方法已经帮我们完成了大量工作 2.main函数将所有发送到/路径下的请求和handler函数关联起来,/开头的请求其实就是所有发送到当前站点上的 ...

  8. Centeros7下安装Mysql 2018最新版,非常简单

    下载Mysql的rpm安装包 shell> wget http://dev.mysql.com/get/ mysql-community-release-el7-5.noarch.rpm安装sh ...

  9. K:图的存储结构

      常用的图的存储结构主要有两种,一种是采用数组链表(邻接表)的方式,一种是采用邻接矩阵的方式.当然,图也可以采用十字链表或者边集数组的方式来进行表示,但由于不常用,为此,本博文不对其进行介绍. 邻接 ...

  10. Linux下svn的安装与部署

    最近工作碰到一个问题,我和一个同伙负责开发一个管理系统,基于原来的代码上进行修改,每当他修改之后,我要再修改都要和他确定是不是最新的文件,才能进行修改.非常影响工作的效率,所以在网上找了关于svn的使 ...