android -- 蓝牙 bluetooth (五)接电话与听音乐

news/2024/12/29 20:54:06/
前段时间似乎所有的事情都赶在一起,回家、集体出游、出差,折腾了近一个月,终于算暂时清静了,但清静只是暂时,估计马上又要出差了,所以赶紧把蓝牙这一部分的文章了结下,按之前提到的目录,本文是关于蓝牙接打电话和听音乐的流程分析,对应蓝牙HFP/A2DP的profile,由于这部分也算是蓝牙的经典功能,所以代码流程并不是很复杂,当然不复杂仅是对于代码调用流程而言,对于HFP/A2DP协议相关的东东还没有精力去看,其难易程序也无法评价。下面从两个点HFP与A2DP来展开本文的代码跟踪:

正文开始之前,先说点题外话,在android系统中蓝牙耳机和听筒两者的音频通道是不一样的,使用蓝牙耳机接听电话和听音乐不仅涉及到本文下面提到的流程,更要牵扯的音频通道的切换,这是一个相对比较复杂的过程,android的音频系统相关内容可不算少,个人感觉多少了下解相关知识可能有助于我们更好的蓝牙这部分功能,不过本文的主题当然还是下面两个。

      1.蓝牙耳机接听电话

这个就对应HFP( Hands-freeProfile),Free your Hand,蓝牙的初衷之一。先来看这个功能的场景,手机来电,手机与蓝牙耳机已连接,这时会优先触发蓝牙接听电话的代码流程,起步代码在phone\src\com\android\phone\nCallScreen.java的connectBluetoothAudio() /disconnectBluetoothAudio(),只看连接部分好了,注意下面代码里的注释,
      /* package */ void connectBluetoothAudio() {if (VDBG) log("connectBluetoothAudio()...");if (mBluetoothHeadset != null) {// TODO(BT) check returnmBluetoothHeadset.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();}
         接下来就跳到蓝牙应用的管辖范围,代码在packages/apps/Bluetooth/src/com/android/bluetooth/hfp/HeadsetService.java,
        public boolean connectAudio() {HeadsetService service = getService();if (service == null) return false;return service.connectAudio();}
        很明显下一个目标是HeadsetService,直接看具体实现,这部分代码跳转都比较清晰,下面代码会先判断当前状态是否正确,关于HeadsetStateMachine几个状态可以参持这个/packages/apps/Bluetooth/src/com/android/bluetooth/hfp/HeadsetStateMachine.java的最前的代码注释。
   boolean connectAudio() {// TODO(BT) BLUETOOTH or BLUETOOTH_ADMIN permissionenforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");if (!mStateMachine.isConnected()) {return false;}if (mStateMachine.isAudioOn()) {return false;}mStateMachine.sendMessage(HeadsetStateMachine.CONNECT_AUDIO);return true;}
       走进HeadsetStateMachine状态机,找到CONNECT_AUDIO分支,就看带Native的方法connectAudioNative(getByteAddress(mCurrentDevice));
static jboolean connectAudioNative(JNIEnv *env, jobject object, jbyteArray address) {jbyte *addr;bt_status_t status;if (!sBluetoothHfpInterface) return JNI_FALSE;addr = env->GetByteArrayElements(address, NULL);if (!addr) {jniThrowIOException(env, EINVAL);return JNI_FALSE;}
//连接在这里if ( (status = sBluetoothHfpInterface->connect_audio((bt_bdaddr_t *)addr)) !=  BT_STATUS_SUCCESS) {ALOGE("Failed HF audio connection, status: %d", status);}env->ReleaseByteArrayElements(address, addr, 0);return (status == BT_STATUS_SUCCESS) ? JNI_TRUE : JNI_FALSE;
}
       上面代码还可以进一步跟到下面/external/bluetooth/bluedroid/btif/src/btif_hf.c,到了这里其实流程已经结束了,对于这里消息流转估计要放到以后再写了
static bt_status_t connect_audio( bt_bdaddr_t *bd_addr )
{CHECK_BTHF_INIT();if (is_connected(bd_addr)){BTA_AgAudioOpen(btif_hf_cb.handle);/* Inform the application that the audio connection has been initiated successfully */btif_transfer_context(btif_in_hf_generic_evt, BTIF_HFP_CB_AUDIO_CONNECTING,(char *)bd_addr, sizeof(bt_bdaddr_t), NULL);return BT_STATUS_SUCCESS;}return BT_STATUS_FAIL;
}

 2.在蓝牙列表中连接蓝牙耳机

A2dp的连接过程,在蓝牙搜索结果列表连接一个蓝牙耳机,既然是从设备列表开始,所以起步代码自然是这个了
  DevicePickerFragment.java (settings\src\com\android\settings\bluetooth)     3884     2013-6-26void onClicked() {int bondState = mCachedDevice.getBondState();if (mCachedDevice.isConnected()) {askDisconnect();} else if (bondState == BluetoothDevice.BOND_BONDED) {mCachedDevice.connect(true);} .......}void connect(boolean connectAllProfiles) {if (!ensurePaired()) {  //要先确保配对return;}mConnectAttempted = SystemClock.elapsedRealtime();connectWithoutResettingTimer(connectAllProfiles);//没别的了,只能看到这里}
      代码路径这里packages/apps/Settings/src/com/android/settings/bluetooth/CachedBluetoothDevice.java,具体代码看下面
  // Try to initialize the profiles if they were not............// Reset the only-show-one-error-dialog tracking variablemIsConnectingErrorPossible = true;int preferredProfiles = 0;for (LocalBluetoothProfile profile : mProfiles) {if (connectAllProfiles ? profile.isConnectable() : profile.isAutoConnectable()) {if (profile.isPreferred(mDevice)) {++preferredProfiles;connectInt(profile);//连接在这里,}}}.............
       connectInt的实现很简单,直接跳过看里面的profile.connect(mDevice),这里的profile是指A2dpProfile,所以connet()方法的具体实现在
    public boolean connect(BluetoothDevice device) {if (mService == null) return false;List<BluetoothDevice> sinks = getConnectedDevices();if (sinks != null) {for (BluetoothDevice sink : sinks) {mService.disconnect(sink);}}return mService.connect(device);}
        下面是 BluetoothA2dp.java (frameworks\base\core\java\android\bluetooth)  ,为什么是这样看下这个private BluetoothA2dp mService;就知道了
  public boolean connect(BluetoothDevice device) {if (mService != null && isEnabled() &&isValidDevice(device)) {try {return mService.connect(device);} catch (RemoteException e) {Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));return false;}}...........return false;Binder跳转public boolean connect(BluetoothDevice device) {A2dpService service = getService();if (service == null) return false;return service.connect(device);}}
        之后的跳转和第一部分蓝牙接听电话跳转过程类似,就不重复了,最后会来到packages/apps/Bluetooth/jni/com_android_bluetooth_a2dp.cpp的connectA2dpNative,同样到下面的代码,我们能看到的开放的代码也就是这些,再下面要看vendor的具体实现了。
  static jboolean connectA2dpNative(JNIEnv *env, jobject object, jbyteArray address) {jbyte *addr;bt_bdaddr_t * btAddr;bt_status_t status;ALOGI("%s: sBluetoothA2dpInterface: %p", __FUNCTION__, sBluetoothA2dpInterface);if (!sBluetoothA2dpInterface) return JNI_FALSE;addr = env->GetByteArrayElements(address, NULL);btAddr = (bt_bdaddr_t *) addr;if (!addr) {jniThrowIOException(env, EINVAL);return JNI_FALSE;}if ((status = sBluetoothA2dpInterface->connect((bt_bdaddr_t *)addr)) != BT_STATUS_SUCCESS) {ALOGE("Failed HF connection, status: %d", status);}env->ReleaseByteArrayElements(address, addr, 0);return (status == BT_STATUS_SUCCESS) ? JNI_TRUE : JNI_FALSE;
}
       那到此为止,本文关于蓝牙耳机与蓝牙接听电话的流程分析也就结束了,同时蓝牙这一系列的文章也暂时结束,当然后续依然会关注蓝牙。本系列的第一篇文章标题是 入门,现在想想,这五篇文章下来也不过是刚刚入门而已,协议部分更是没怎么涉及呢,对于蓝牙BT需要深入研究的地方还有很多,仅希望这五篇文章可以帮你快速了解android蓝牙代码流程,回顾以前四篇文章请点击链接:
android -- 蓝牙 bluetooth (一) 入门
android -- 蓝牙 bluetooth (二) 打开蓝牙
android -- 蓝牙 bluetooth (三)搜索蓝牙
android -- 蓝牙 bluetooth (四)OPP文件传输    
最后感谢在前面文章中网友的热心回复与纠正,学习路上一起分享是快乐的。谢谢!


http://www.ppmy.cn/news/378188.html

相关文章

Android 蓝牙 A2dp 播放音乐流程再探究 --- setDeviceConnectionStateInt(二)

同学,别退出呀,我可是全网最牛逼的 Android 蓝牙分析博主,我写了上百篇蓝牙文章,请点击下面了解本专栏,进入本博主主页看看再走呗,一定不会让你后悔的,记得一定要去看主页置顶文章哦。 一、概述 我们来看下a2dp下面的播放场景吧。a2dp是蓝牙用来播放音乐的协议,正常情…

Android音乐播放器制作(一)扫描本地音乐显示在手机上

思路 首先是扫描本地所有的音频文件&#xff0c;然后全部装进集合当中&#xff0c;接下来就是用ListView展示在屏幕上&#xff0c;大概就是这几个步骤了&#xff0c;接下来细讲 创建一个容器 进行过数据解析的朋友都应该知道JavaBean吧&#xff0c;用来装载解析出来的数据&…

蓝牙音乐之A2DP

蓝牙音乐之A2DP 本篇文章简单聊聊蓝牙音乐涉及到的A2DP协议&#xff0c;大家平常使用蓝牙耳机听音乐就是通过该协议实现的&#xff0c;此协议有时也被称为媒体音频协议。通过手机蓝牙设置中已连接的设备可以查看具体连接的哪些协议&#xff0c;那其中的媒体音频那一项就对应于本…

基于CK6855M1设计的离线语音识别蓝牙音乐球泡灯的设计

一.方案概述 随着人类文明的进步&#xff0c;21世纪的人们对物质的要求也越来越高。在灯具照明市场层面&#xff0c;很多传统灯具厂的经营状况也越来越不乐观&#xff0c;其原因是&#xff1a; 1.传统灯具产品生产制造门款低&#xff0c;利润薄 2.竞争激烈&#xff0c;各生产…

蓝牙音乐播放芯片改怎么选型?

蓝牙音乐播放芯片改怎么选型&#xff1f; 随着蓝牙技术的成长&#xff0c;市面上的蓝牙方案不管是在技术层面还是市场占用率方面&#xff0c;这些年都发生了翻天覆地的变化。现在市面上的蓝牙芯片多而且杂&#xff0c;那么&#xff0c;蓝牙音乐播放芯片怎么选择呢&#xff1f;…

BLE-HID 音乐/相机控制

以SDK下自带的 HID 鼠标示例为例说明&#xff1a; 音乐控制&#xff1a; 代码中 main中调用了services_init初始化几个服务。 其中调用hids_init 来初始化hid服务 函数中首先定义了 static uint8_t rep_map_data[] 这个数组变量。 这个变量里面存的值就是 报告描述符&…

BLE HID控制手机相机或音乐

BLE HID涉及到设备管理。具体内容看<<设备管理>>部分。 BLE HID涉及到白名单。具体内容看<<白名单>>部分。 BLE HID涉及到定向广播。具体内容看<<定向广播>>部分。 硬件平台&#xff1a;官方52开发板 参考例程&#xff1a;C:..\nRF5_SD…