一权限:
(1)定位权限
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
(2)蓝牙权限
<uses-permission android:name="android.permission.BLUETOOTH"/>
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN"/>
<uses-permission android:name="android.permission.BLUETOOTH_PRIVILEGED"/>
二:蓝牙状态
//蓝牙状态
private void BluetoothState(){
BluetoothAdapter bluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
if(bluetoothAdapter!=null){
//设备支持蓝牙
}else {
//设备不支持蓝牙
return;
}
//蓝牙是否是开启的
boolean isOppenBluetooth = bluetoothAdapter.isEnabled();
bluetoothAdapter.enable(); //开启蓝牙
bluetoothAdapter.disable();//关闭蓝牙
}
三:扫描蓝牙设备
//发起蓝牙设备扫描
private void startScanningBluetoothDevice(){
BluetoothAdapter bluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
if(bluetoothAdapter!=null){
if(bluetoothAdapter.isDiscovering()){
bluetoothAdapter.cancelDiscovery(); //如果正处于扫描状态就先取消扫描
}
bluetoothAdapter.startDiscovery();
}
}
四:接收扫描到的蓝牙设备
//广播接收器,接收扫描到的蓝牙设备
private BroadcastReceiver mReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
if(action.equals(BluetoothDevice.ACTION_FOUND)){
BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
String name = device.getName();
String address = device.getAddress();
}else if(action.equals(BluetoothAdapter.ACTION_DISCOVERY_FINISHED)){
//扫描过程结束,一般情况下一个扫描过程持续12秒左右
}
}
};
//注册发起扫描的广播
private void registeredBluetoothScanningBroadcast(){
IntentFilter filter = new IntentFilter();
filter.addAction(BluetoothDevice.ACTION_FOUND);
filter.addAction(BluetoothAdapter.ACTION_DISCOVERY_FINISHED);
registerReceiver(mReceiver,filter);
}
注:蓝牙扫描过程是耗时操作,并且扫描的结果返回是异步的所以广播机制来获取扫描到的蓝牙设备,接收到的结果是BluetoothDevice对象,通过该对象可以做一些简单的操作比如:发起配对请求,发起链接,发起断开等操作。
五:发起蓝牙设备的链接
//注册链接状态的回调
private BluetoothGattCallback callback = new BluetoothGattCallback() {
@Override
public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {
super.onConnectionStateChange(gatt, status, newState);
//设备链接过程中的回调,newState值为2的时候表示链接成功
}
};
//去链接蓝牙设备
private boolean connectBluetoothDevice(String address){
BluetoothAdapter bluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
if(bluetoothAdapter==null){
return false;
}
final BluetoothDevice device = bluetoothAdapter.getRemoteDevice(address);
//发起Gatt链接
BluetoothGatt gatt;
gatt = device.connectGatt(getApplicationContext(), false,callback);
if(gatt!=null){
return true;
}
return false;
}
六:蓝牙设备的断开可通过广播事件接收到断开的状态
//广播接收器,接收断开蓝牙设备的广播
private BroadcastReceiver mBluetoothDissConnectedReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
if(action.equals(BluetoothAdapter.ACTION_CONNECTION_STATE_CHANGED)){
int bluetoothState = intent.getIntExtra(BluetoothAdapter.EXTRA_CONNECTION_STATE,0);
BluetoothDevice device = (BluetoothDevice) intent.getExtra(BluetoothDevice.EXTRA_DEVICE);
if(bluetoothState == 0){
//断开
}
}
}
};
//注册蓝牙断开的的广播
private void registeredBluetoothDissConnectedBroadcast(){
IntentFilter filter = new IntentFilter();
filter.addAction(BluetoothAdapter.ACTION_CONNECTION_STATE_CHANGED);
registerReceiver(mBluetoothDissConnectedReceiver,filter);
}
七:总结一下蓝牙相关的各种状态的广播:
intentFilter.addAction(BluetoothDevice.ACTION_FOUND);//搜索蓝压设备,每搜到一个设备发送一条广播
intentFilter.addAction(BluetoothDevice.ACTION_BOND_STATE_CHANGED); //配对开始时,配对成功时
intentFilter.addAction(BluetoothDevice.ACTION_ACL_CONNECTED); //配对时,发起连接
intentFilter.addAction(BluetoothDevice.ACTION_ACL_DISCONNECT_REQUESTED);
intentFilter.addAction(BluetoothDevice.ACTION_ACL_DISCONNECTED); //配对结束时,断开连接
intentFilter.addAction(PAIRING_REQUEST); //配对请求(Android.bluetooth.device.action.PAIRING_REQUEST)
intentFilter.addAction(BluetoothAdapter.ACTION_DISCOVERY_STARTED); //开始搜索
intentFilter.addAction(BluetoothAdapter.ACTION_DISCOVERY_FINISHED); //搜索结束。重新搜索时,会先终止搜索
intentFilter.addAction(BluetoothAdapter.ACTION_REQUEST_DISCOVERABLE);
intentFilter.addAction(BluetoothAdapter.ACTION_STATE_CHANGED); //本机开启、关闭蓝牙开关
intentFilter.addAction(BluetoothAdapter.ACTION_CONNECTION_STATE_CHANGED); //蓝牙设备连接或断开
intentFilter.addAction(BluetoothAdapter.ACTION_LOCAL_NAME_CHANGED); //更改蓝牙名称,打开蓝牙时,可能会调用多次
intentFilter.addAction(BluetoothAdapter.ACTION_REQUEST_DISCOVERABLE);
intentFilter.addAction(BluetoothAdapter.ACTION_REQUEST_ENABLE);
intentFilter.addAction(BluetoothAdapter.ACTION_SCAN_MODE_CHANGED); //搜索模式改变
其中BluetoothAdapter.ACTION_CONNECTION_STATE_CHANGED
和 BluetoothAdapter.ACTION_STATE_CHANGED 这两个Intent广播。
那么这两个广播Intent的区别是什么呢?只用其中一个可以吗?
BluetoothAdapter.ACTION_CONNECTION_STATE_CHANGED
:指的是本地蓝牙适配器的连接状态的发生改变(比如没有关闭本机蓝牙开关时,另外一个配对设备自己把连接断开)BluetoothAdapter.ACTION_STATE_CHANGED
:指的是本地蓝牙适配器的状态已更改。 例如,蓝牙开关打开或关闭。
换句话说,一个是用于连接状态的变化,另一个用于蓝牙适配器本身的状态变化。经过测试发现,如果只使用BluetoothAdapter.ACTION_CONNECTION_STATE_CHANGED
监听广播,则会接收不到“主动关闭本机蓝牙开关”的广播事件。但只是用BluetoothAdapter.ACTION_STATE_CHANGED
的话,很明显这时候蓝牙设备并未真正配对。
八.总结一下蓝牙设备的风格及类型
/**
* 获取蓝牙设备的风格
* @param device
* @return
*/
public static String getBluetoothStyleString(BluetoothDevice device) {
String style = "未知...";
if (device != null) {
int styleMajor = device.getBluetoothClass().getMajorDeviceClass();
switch (styleMajor) {
case BluetoothClass.Device.Major.AUDIO_VIDEO:
style = "音频设备";
break;
case BluetoothClass.Device.Major.COMPUTER:
style = "电脑";
break;
case BluetoothClass.Device.Major.HEALTH://健康状况
style = "健康状况";
break;
case BluetoothClass.Device.Major.IMAGING://镜像,映像
style = "镜像";
break;
case BluetoothClass.Device.Major.MISC:
style = "麦克风";
break;
case BluetoothClass.Device.Major.NETWORKING:
style = "网络";
break;
case BluetoothClass.Device.Major.PERIPHERAL:
style = "外部设备";
break;
case BluetoothClass.Device.Major.PHONE:
style = "电话";
break;
case BluetoothClass.Device.Major.TOY:
style = "玩具";
break;
case BluetoothClass.Device.Major.UNCATEGORIZED:
style = "未知的";
break;
case BluetoothClass.Device.Major.WEARABLE:
style = "穿戴设备";
break;
default:
break;
}
}
return style;
}
/**
* 获取蓝牙设备的类型
*
* @param device
* @return
*/
public static String getBluetoothTypeString(BluetoothDevice device) {
String type = "未知的...";
if (device != null) {
int cls = device.getBluetoothClass().getDeviceClass();
switch (cls) {
case BluetoothClass.Device.AUDIO_VIDEO_CAMCORDER:
type = "录像机";
break;
case BluetoothClass.Device.AUDIO_VIDEO_CAR_AUDIO:
type = "车载设备";
break;
case BluetoothClass.Device.AUDIO_VIDEO_HANDSFREE:
type = "蓝牙耳机";
break;
case BluetoothClass.Device.AUDIO_VIDEO_LOUDSPEAKER:
type = "扬声器";
break;
case BluetoothClass.Device.AUDIO_VIDEO_MICROPHONE:
type = "麦克风";
break;
case BluetoothClass.Device.AUDIO_VIDEO_PORTABLE_AUDIO:
type = "打印机";
break;
case BluetoothClass.Device.AUDIO_VIDEO_SET_TOP_BOX:
type = "BOX";
break;
case BluetoothClass.Device.AUDIO_VIDEO_UNCATEGORIZED:
type = "未知的";
break;
case BluetoothClass.Device.AUDIO_VIDEO_VCR:
type = "录像机";
break;
case BluetoothClass.Device.AUDIO_VIDEO_VIDEO_CAMERA:
type = "照相机录像机";
break;
case BluetoothClass.Device.AUDIO_VIDEO_VIDEO_CONFERENCING:
type = "conferencing";
break;
case BluetoothClass.Device.AUDIO_VIDEO_VIDEO_DISPLAY_AND_LOUDSPEAKER:
type = "显示器和扬声器";
break;
case BluetoothClass.Device.AUDIO_VIDEO_VIDEO_GAMING_TOY:
type = "游戏";
break;
case BluetoothClass.Device.AUDIO_VIDEO_VIDEO_MONITOR:
type = "可穿戴设备";
break;
case BluetoothClass.Device.PHONE_CELLULAR:
type = "手机";
break;
case BluetoothClass.Device.PHONE_CORDLESS:
type = "无线设备";
break;
case BluetoothClass.Device.PHONE_ISDN:
type = "手机服务数据网";
break;
case BluetoothClass.Device.PHONE_MODEM_OR_GATEWAY:
type = "手机调节器";
break;
case BluetoothClass.Device.PHONE_SMART:
type = "手机卫星";
break;
case BluetoothClass.Device.PHONE_UNCATEGORIZED:
type = "未知手机";
break;
case BluetoothClass.Device.WEARABLE_GLASSES:
type = "可穿戴眼睛";
break;
case BluetoothClass.Device.WEARABLE_HELMET:
type = "可穿戴头盔";
break;
case BluetoothClass.Device.WEARABLE_JACKET:
type = "可穿戴上衣";
break;
case BluetoothClass.Device.WEARABLE_PAGER:
type = "客串点寻呼机";
break;
case BluetoothClass.Device.WEARABLE_UNCATEGORIZED:
type = "未知的可穿戴设备";
break;
case BluetoothClass.Device.WEARABLE_WRIST_WATCH:
type = "手腕监听设备";
break;
case BluetoothClass.Device.TOY_CONTROLLER:
type = "可穿戴设备";
break;
case BluetoothClass.Device.TOY_DOLL_ACTION_FIGURE:
type = "玩具doll_action_figure";
break;
case BluetoothClass.Device.TOY_GAME:
type = "游戏";
break;
case BluetoothClass.Device.TOY_ROBOT:
type = "玩具遥控器";
break;
case BluetoothClass.Device.TOY_UNCATEGORIZED:
type = "玩具未知设备";
break;
case BluetoothClass.Device.TOY_VEHICLE:
type = "vehicle";
break;
case BluetoothClass.Device.HEALTH_BLOOD_PRESSURE:
type = "健康状态-血压";
break;
case BluetoothClass.Device.HEALTH_DATA_DISPLAY:
type = "健康状态数据";
break;
case BluetoothClass.Device.HEALTH_GLUCOSE:
type = "健康状态葡萄糖";
break;
case BluetoothClass.Device.HEALTH_PULSE_OXIMETER:
type = "健康状态脉搏血氧计";
break;
case BluetoothClass.Device.HEALTH_PULSE_RATE:
type = "健康状态脉搏速来";
break;
case BluetoothClass.Device.HEALTH_THERMOMETER:
type = "健康状态体温计";
break;
case BluetoothClass.Device.HEALTH_WEIGHING:
type = "健康状态体重";
break;
case BluetoothClass.Device.HEALTH_UNCATEGORIZED:
type = "未知健康状态设备";
break;
case BluetoothClass.Device.COMPUTER_DESKTOP:
type = "电脑桌面";
break;
case BluetoothClass.Device.COMPUTER_HANDHELD_PC_PDA:
type = "手提电脑或Pad";
break;
case BluetoothClass.Device.COMPUTER_LAPTOP:
type = "便携式电脑";
break;
case BluetoothClass.Device.COMPUTER_PALM_SIZE_PC_PDA:
type = "微型电脑";
break;
case BluetoothClass.Device.COMPUTER_SERVER:
type = "电脑服务";
break;
case BluetoothClass.Device.COMPUTER_UNCATEGORIZED:
type = "未知的电脑设备";
break;
case BluetoothClass.Device.COMPUTER_WEARABLE:
type = "可穿戴的电脑";
break;
case BluetoothClass.Device.AUDIO_VIDEO_HEADPHONES:
type = "头戴式受话器";
break;
case BluetoothClass.Device.AUDIO_VIDEO_HIFI_AUDIO:
type = "高保真音频设备";
break;
case BluetoothClass.Device.AUDIO_VIDEO_WEARABLE_HEADSET:
type = "可穿戴耳机";
break;
default:
break;
}
}
return type;
}
九. 蓝牙BluetoothManagerService
运行在system进程中的服务,手机系统开机启动后即运行。BluetoothManagerService是开机后没有操作蓝牙情况下 唯一一个运行的蓝牙进程,它控制着AdapterService的绑定与解绑,且是蓝牙打开和关闭的最早的入口。SystemUI中的蓝牙开关,飞行模式控制的蓝牙开与关,Settings中的蓝牙开关控制的蓝牙的开与关都是经过BluetoothManagerService控制,具体执行的流程如下:
1.enable
private void sendEnableMsg(boolean quietMode, int reason, String packageName) {
mHandler.sendMessage(mHandler.obtainMessage(MESSAGE_ENABLE, quietMode ? 1 : 0, 0));
addActiveLog(reason, packageName, true);
mLastEnabledTime = SystemClock.elapsedRealtime();
}
2.disable
private void sendDisableMsg(int reason, String packageName) {
mHandler.sendMessage(mHandler.obtainMessage(MESSAGE_DISABLE));
addActiveLog(reason, packageName, false);
}
具体是哪块按钮控制的可以用int reason, String packageName这两个参数做区别。
蓝牙相关Log总结:
1.蓝牙耳机暂停功/播放键功能,MediaSessionService的日志:
暂停log:
09-17 10:06:13.015 1058 4674 D MediaSessionService: Sending KeyEvent { action=ACTION_DOWN, keyCode=KEYCODE_MEDIA_PAUSE, scanCode=0, metaState=0, flags=0x0, repeatCount=0, eventTime=0, downTime=0, deviceId=-1, source=0x0, displayId=0 } to cn.kuwo.player/MediaSessionHelper-cn.kuwo.player (userId=0)
09-17 10:06:13.059 1058 4674 D MediaSessionService: Sending KeyEvent { action=ACTION_UP, keyCode=KEYCODE_MEDIA_PAUSE, scanCode=0, metaState=0, flags=0x0, repeatCount=0, eventTime=0, downTime=0, deviceId=-1, source=0x0, displayId=0 } to cn.kuwo.player/MediaSessionHelper-cn.kuwo.player (userId=0)
播放log:
09-17 10:06:16.598 1058 2853 D MediaSessionService: Sending KeyEvent { action=ACTION_DOWN, keyCode=KEYCODE_MEDIA_PLAY, scanCode=0, metaState=0, flags=0x0, repeatCount=0, eventTime=0, downTime=0, deviceId=-1, source=0x0, displayId=0 } to cn.kuwo.player/MediaSessionHelper-cn.kuwo.player (userId=0)
09-17 10:06:16.626 1058 2379 D MediaSessionService: Sending KeyEvent { action=ACTION_UP, keyCode=KEYCODE_MEDIA_PLAY, scanCode=0, metaState=0, flags=0x0, repeatCount=0, eventTime=0, downTime=0, deviceId=-1, source=0x0, displayId=0 } to cn.kuwo.player/MediaSessionHelper-cn.kuwo.player (userId=0)
2.蓝牙关闭打开的日志:
蓝牙被打开,并且可以看到是被com.android.systemui应用给打开了
BluetoothManagerService: enable(com.android.systemui): mBluetooth =null mBinding = false mState = OFF
蓝牙关闭
BluetoothManagerService: disable(): mBluetooth = android.bluetooth.IBluetooth$Stub$Proxy@43a232a mBinding = false
3.分享数据到蓝牙
首先被启动的界面是:
com.android.bluetooth/.opp.BluetoothOppLauncherActivity
里面有各种操作逻辑,数据传输,文件大小限制等。
4.蓝牙耳机所使用的协议的关键字:
10-23 22:15:29.963 28795 28795 D a2dp_codec: createCodec: codec SBC
10-23 22:15:29.970 28795 28795 D a2dp_codec: createCodec: codec SBC SINK
10-23 22:15:29.970 28795 28795 I a2dp_codec: init: initialized Sink codec SBC(Sink)
5.铃声播报通道log:
(1)铃声走手机听筒:10-20 09:53:09.109 20730 22136 I AudioManager: In isSpeakerphoneOn(), calling application: com.tencent.mobileqq
(2)铃声走蓝牙耳机:0-20 09:53:10.396 20730 22152 I AudioManager: In startbluetoothSco(), calling application: com.tencent.mobileqq
十:在蓝牙列表中连接a2dp设备(耳机,音响)的流程
A2dp的连接过程,在蓝牙搜索结果列表连接一个蓝牙耳机,既然是从设备列表开始,所以起步代码自然是这个了:
DevicePickerFragment.java (settings\src\com\android\settings\bluetooth) 3884 2013-6-26
void 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);//没别的了,只能看到这里
}
mCachedDevice是CachedBluetoothDevice.java的实例,具体连接方法就要看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 variable
mIsConnectingErrorPossible = 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;
到此为止,蓝牙耳机的连接过程就梳理完毕了。
十二:梳理蓝牙接听电话流程
在android系统中蓝牙耳机和听筒两者的音频通道是不一样的,使用蓝牙耳机接听电话和听音乐不仅涉及本文提到的流程,更牵扯到音频通道的切换,这是一个相对比较复杂的过程,android的音频系统相关内容可不算少,个人觉得多少了解下相关知识更有助于我们更好的理解蓝牙这部分功能。
蓝牙耳机接听电话
这个就对应HFP(Hands-freeProfile),Free your Hand,蓝牙的初衷之一。先来看看这个功能的场景,手机来电,手机与蓝牙耳机已经连接,这时会优先触发蓝牙接听电话的代码流程,起步代码在phone\src\com\android\phone\nCallScreen.java的connectBluetoothAudio() /disconnectBluetoothAudio(),只看连接部分好了,注意下面代码里的注释,
接下来就跳到蓝牙应用的管辖范围,代码在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 permission
enforceCallingOrSelfPermission(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;
}