Android 8 蓝牙 A2DP流程
记录一下蓝牙A2DP的流程
packages\apps\Settings\src\com\android\settings\bluetooth\BluetoothPairingDetail.java
@Override
void onDevicePreferenceClick(BluetoothDevicePreference btPreference) {
disableScanning();
super.onDevicePreferenceClick(btPreference);
}
packages\apps\Settings\src\com\android\settings\bluetooth\DeviceListPreferenceFragment.java
void onDevicePreferenceClick(BluetoothDevicePreference btPreference) {
btPreference.onClicked();
}
packages\apps\Settings\src\com\android\settings\bluetooth\BluetoothDevicePreference.java
void onClicked() {
Context context = getContext();
// 获取连接状态
int bondState = mCachedDevice.getBondState();
final MetricsFeatureProvider metricsFeatureProvider =
FeatureFactory.getFactory(context).getMetricsFeatureProvider();
// 已经连接
if (mCachedDevice.isConnected()) {
// 断开连接
metricsFeatureProvider.action(context,
MetricsEvent.ACTION_SETTINGS_BLUETOOTH_DISCONNECT);
askDisconnect();
// 以前连接过,不需要再配对,直接进行连接
} else if (bondState == BluetoothDevice.BOND_BONDED) {
metricsFeatureProvider.action(context,
MetricsEvent.ACTION_SETTINGS_BLUETOOTH_CONNECT);
mCachedDevice.connect(true);
// 没连接过,进行配对,需要连接的双方都同意之后才能连接
} else if (bondState == BluetoothDevice.BOND_NONE) {
metricsFeatureProvider.action(context,
MetricsEvent.ACTION_SETTINGS_BLUETOOTH_PAIR);
if (!mCachedDevice.hasHumanReadableName()) {
metricsFeatureProvider.action(context,
MetricsEvent.ACTION_SETTINGS_BLUETOOTH_PAIR_DEVICES_WITHOUT_NAMES);
}
pair();
}
}
匹配过的设备,进行连接
frameworks\base\packages\SettingsLib\src\com\android\settingslib\bluetooth\CachedBluetoothDevice.java
public void connect(boolean connectAllProfiles) {
// 是否配对过
if (!ensurePaired()) {
return;
}
mConnectAttempted = SystemClock.elapsedRealtime();
connectWithoutResettingTimer(connectAllProfiles);
}
private void connectWithoutResettingTimer(boolean connectAllProfiles) {
// Try to initialize the profiles if they were not.
if (mProfiles.isEmpty()) {
// if mProfiles is empty, then do not invoke updateProfiles. This causes a race
// condition with carkits during pairing, wherein RemoteDevice.UUIDs have been updated
// from bluetooth stack but ACTION.uuid is not sent yet.
// Eventually ACTION.uuid will be received which shall trigger the connection of the
// various profiles
// If UUIDs are not available yet, connect will be happen
// upon arrival of the ACTION_UUID intent.
Log.d(TAG, "No profiles. Maybe we will connect later");
return;
}
// Reset the only-show-one-error-dialog tracking variable
mIsConnectingErrorPossible = true;
int preferredProfiles = 0;
for (LocalBluetoothProfile profile : mProfiles) {
// connectAllProfile传进来的是true
if (connectAllProfiles ? profile.isConnectable() : profile.isAutoConnectable()) {
if (profile.isPreferred(mDevice)) {
++preferredProfiles;
connectInt(profile); // 选择对应的profile进行连接
}
}
}
if (DEBUG) Log.d(TAG, "Preferred profiles = " + preferredProfiles);
if (preferredProfiles == 0) {
connectAutoConnectableProfiles();
}
}
private void connectAutoConnectableProfiles() {
if (!ensurePaired()) {
return;
}
// Reset the only-show-one-error-dialog tracking variable
mIsConnectingErrorPossible = true;
for (LocalBluetoothProfile profile : mProfiles) {
if (profile.isAutoConnectable()) {
profile.setPreferred(mDevice, true);
connectInt(profile);
}
}
}
frameworks\base\packages\SettingsLib\src\com\android\settingslib\bluetooth\CachedBluetoothDevice.java
synchronized void connectInt(LocalBluetoothProfile profile) {
// 判断是否配对过
if (!ensurePaired()) {
return;
}
// 连接
if (profile.connect(mDevice)) {
if (Utils.D) {
Log.d(TAG, "Command sent successfully:CONNECT " + describe(profile));
}
return;
}
Log.i(TAG, "Failed to connect " + profile.toString() + " to " + mName);
}
A2dp的profile
frameworks\base\packages\SettingsLib\src\com\android\settingslib\bluetooth\A2dpProfile.java
public class A2dpProfile implements LocalBluetoothProfile {
public boolean connect(BluetoothDevice device) {
if (mService == null) return false;
List<BluetoothDevice> sinks = getConnectedDevices();
if (sinks != null) {
for (BluetoothDevice sink : sinks) {
if (sink.equals(device)) {
Log.w(TAG, "Connecting to device " + device + " : disconnect skipped");
continue;
}
}
}
// 连接
return mService.connect(device);
}
frameworks\base\core\java\android\bluetooth\BluetoothA2dp.java
@GuardedBy("mServiceLock") private IBluetoothA2dp mService;
public boolean connect(BluetoothDevice device) {
if (DBG) log("connect(" + device + ")");
try {
mServiceLock.readLock().lock();
if (mService != null && isEnabled() &&
isValidDevice(device)) {
return mService.connect(device); // 调用aidl中的方法
}
if (mService == null) Log.w(TAG, "Proxy not attached to service");
return false;
} catch (RemoteException e) {
Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
return false;
} finally {
mServiceLock.readLock().unlock();
}
}
Binder进程间通信,服务端实现方法如下。
packages\apps\Bluetooth\src\com\android\bluetooth\a2dp\A2dpService.java
private static class BluetoothA2dpBinder extends IBluetoothA2dp.Stub
implements IProfileServiceBinder {
public boolean connect(BluetoothDevice device) {
A2dpService service = getService();
if (service == null) return false;
//do not allow new connections with active multicast
if (service.isMulticastOngoing(device)) {
Log.i(TAG,"A2dp Multicast is Ongoing, ignore Connection Request");
return false;
}
return service.connect(device);
}
public boolean connect(BluetoothDevice device) {
if (DBG) Log.d(TAG, "Enter connect");
enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
"Need BLUETOOTH ADMIN permission");
if (getPriority(device) == BluetoothProfile.PRIORITY_OFF) {
return false;
}
ParcelUuid[] featureUuids = device.getUuids();
if ((BluetoothUuid.containsAnyUuid(featureUuids, A2DP_SOURCE_UUID)) &&
!(BluetoothUuid.containsAllUuids(featureUuids ,A2DP_SOURCE_SINK_UUIDS))) {
Log.e(TAG,"Remote does not have A2dp Sink UUID");
return false;
}
int connectionState = BluetoothProfile.STATE_DISCONNECTED;
synchronized(mBtA2dpLock) {
if (mStateMachine != null) {
connectionState = mStateMachine.getConnectionState(device);
}
}
if (connectionState == BluetoothProfile.STATE_CONNECTED ||
connectionState == BluetoothProfile.STATE_CONNECTING) {
return false;
}
// 发送消息给状态机
synchronized(mBtA2dpLock) {
if (mStateMachine != null) {
mStateMachine.sendMessage(A2dpStateMachine.CONNECT, device);
}
}
if (DBG) Log.d(TAG, "Exit connect");
return true;
}
状态机初始状态Disconnect
packages\apps\Bluetooth\src\com\android\bluetooth\a2dp\A2dpStateMachine.java
final class A2dpStateMachine extends StateMachine {
switch(message.what) {
case CONNECT:
BluetoothDevice device = (BluetoothDevice) message.obj;
// 发送广播,状态改变
broadcastConnectionState(device, BluetoothProfile.STATE_CONNECTING,
BluetoothProfile.STATE_DISCONNECTED);
// 进行连接
if (!connectA2dpNative(getByteAddress(device)) ) {
// 连接完成,失败就再次发广播,状态有connecting->disconnect
broadcastConnectionState(device, BluetoothProfile.STATE_DISCONNECTED,
BluetoothProfile.STATE_CONNECTING);
break;
}
synchronized (A2dpStateMachine.this) {
mTargetDevice = device;
transitionTo(mPending); // 切换状态机状态
}
// TODO(BT) remove CONNECT_TIMEOUT when the stack
// sends back events consistently
Message m = obtainMessage(CONNECT_TIMEOUT);
m.obj = device;
sendMessageDelayed(m, CONNECT_TIMEOUT_SEC);
break;
packages\apps\Bluetooth\jni\com_android_bluetooth_a2dp.cpp
static jboolean connectA2dpNative(JNIEnv* env, jobject object,
jbyteArray address) {
ALOGI("%s: sBluetoothA2dpInterface: %p", __func__, sBluetoothA2dpInterface);
if (!sBluetoothA2dpInterface) return JNI_FALSE;
jbyte* addr = env->GetByteArrayElements(address, NULL);
if (!addr) {
jniThrowIOException(env, EINVAL);
return JNI_FALSE;
}
bt_status_t status = sBluetoothA2dpInterface->connect((RawAddress*)addr);
if (status != BT_STATUS_SUCCESS) {
ALOGE("Failed HF connection, status: %d", status);
}
env->ReleaseByteArrayElements(address, addr, 0);
return (status == BT_STATUS_SUCCESS) ? JNI_TRUE : JNI_FALSE;
}
初始化
static void initNative(JNIEnv* env, jobject object,
jobjectArray codecConfigArray,
jint maxA2dpConnection,
jint multiCastState) {
sBluetoothA2dpInterface =
(btav_source_interface_t*)btInf->get_profile_interface(
BT_PROFILE_ADVANCED_AUDIO_ID);
if (sBluetoothA2dpInterface == NULL) {
ALOGE("Failed to get Bluetooth A2DP Interface");
return;
}
system\bt\btif\src\bluetooth.cc
static const void* get_profile_interface(const char* profile_id) {
LOG_INFO(LOG_TAG, "%s: id = %s", __func__, profile_id);
/* sanity check */
if (interface_ready() == false) return NULL;
/* check for supported profile interfaces */
if (is_profile(profile_id, BT_PROFILE_HANDSFREE_ID))
return btif_hf_get_interface();
if (is_profile(profile_id, BT_PROFILE_HANDSFREE_CLIENT_ID))
return btif_hf_client_get_interface();
if (is_profile(profile_id, BT_PROFILE_SOCKETS_ID))
return btif_sock_get_interface();
if (is_profile(profile_id, BT_PROFILE_PAN_ID))
return btif_pan_get_interface();
// audio有两个,一个是作为source用
if (is_profile(profile_id, BT_PROFILE_ADVANCED_AUDIO_ID))
return btif_av_get_src_interface();
// audio, 作为sink用
if (is_profile(profile_id, BT_PROFILE_ADVANCED_AUDIO_SINK_ID))
return btif_av_get_sink_interface();
system\bt\btif\src\btif_av.cc
const btav_source_interface_t* btif_av_get_src_interface(void) {
BTIF_TRACE_EVENT("%s", __func__);
return &bt_av_src_interface;
}
static const btav_source_interface_t bt_av_src_interface = {
sizeof(btav_source_interface_t),
init_src,
src_connect_sink, // 音频源连接sink,这个应该是对音频进行编码,然后输出
disconnect,
codec_config_src,
cleanup_src,
allow_connection,
select_audio_device,
};
static const btav_sink_interface_t bt_av_sink_interface = {
sizeof(btav_sink_interface_t),
init_sink,
sink_connect_src, // sink连接音频源, sink接受音频数据,解码进行播放
disconnect,
cleanup_sink,
update_audio_focus_state,
update_audio_track_gain,
};
连接成功之后,底层发送事件。
packages\apps\Bluetooth\src\com\android\bluetooth\a2dp\A2dpStateMachine.java
private void onConnectionStateChanged(int state, byte[] address) {
log("Enter onConnectionStateChanged() ");
StackEvent event = new StackEvent(EVENT_TYPE_CONNECTION_STATE_CHANGED);
event.valueInt = state;
event.device = getDevice(address);
sendMessage(STACK_EVENT, event);
log("Exit onConnectionStateChanged() ");
}
private class Disconnected extends State {
case STACK_EVENT:
StackEvent event = (StackEvent) message.obj;
switch (event.type) {
case EVENT_TYPE_CONNECTION_STATE_CHANGED:
processConnectionEvent(event.valueInt, event.device);
break;
default:
loge("Unexpected stack event: " + event.type);
break;
private void processConnectionEvent(int state, BluetoothDevice device) {
log("processConnectionEvent state = " + state +
", device = " + device);
switch (state) {
...
case CONNECTION_STATE_CONNECTED:
logw("A2DP Connected from Disconnected state");
if (okToConnect(device)) {
logi("Incoming A2DP accepted");
synchronized (A2dpStateMachine.this) {
if (!mConnectedDevicesList.contains(device)) {
mConnectedDevicesList.add(device);
log( "device " + device.getAddress() +
" is adding in Disconnected state");
}
mCurrentDevice = device;
transitionTo(mConnected);
}
broadcastConnectionState(device, BluetoothProfile.STATE_CONNECTED,
BluetoothProfile.STATE_DISCONNECTED);
} else {
//reject the connection and stay in Disconnected state itself
logi("Incoming A2DP rejected");
disconnectA2dpNative(getByteAddress(device));
}
break;
private void broadcastConnectionState(BluetoothDevice device, int newState, int prevState) {
log("Enter broadcastConnectionState() ");
int delay = 0;
if (mDummyDevice == null) {
Log.i(TAG, "Setting the dummy device for audio service: " + device);
String dummyAddress = "FA:CE:FA:CE:FA:CE";
mDummyDevice = mAdapter.getRemoteDevice(dummyAddress);
}
if ((newState == BluetoothProfile.STATE_CONNECTED) ||
(newState == BluetoothProfile.STATE_DISCONNECTING)) {
if (mConnectedDevicesList.size() == 1) {
Log.d("A2dpStateMachine", "broadcasting connection state");
delay = mAudioManager.setBluetoothA2dpDeviceConnectionState(mDummyDevice,
newState, BluetoothProfile.A2DP);
} else {
Log.d("A2dpStateMachine", "DualA2dp: not broadcasting connected/disconnecting state");
}
} else if ((newState == BluetoothProfile.STATE_DISCONNECTED) ||
(newState == BluetoothProfile.STATE_CONNECTING)) {
if (mConnectedDevicesList.size() == 0) {
Log.d("A2dpStateMachine", "broadcasting connection state");
delay = mAudioManager.setBluetoothA2dpDeviceConnectionState(mDummyDevice,
newState, BluetoothProfile.A2DP);
} else {
Log.d("A2dpStateMachine", "DualA2dp: not broadcasting connecting/disconnected state");
}
}
if (mCodecNotifPending && newState == BluetoothProfile.STATE_CONNECTED) {
Log.i(TAG, "There was pending codec config change, dispatching now. " + "device: " + device);
Log.i(TAG, "Sending broadcast with device " + mDummyDevice);
Intent intent = new Intent(BluetoothA2dp.ACTION_CODEC_CONFIG_CHANGED);
intent.putExtra(BluetoothCodecStatus.EXTRA_CODEC_STATUS, mCodecStatus);
intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
intent.addFlags(Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND);
intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
intent.putExtra(BluetoothDevice.EXTRA_DEVICE, mDummyDevice);
mAudioManager.handleBluetoothA2dpDeviceConfigChange(mDummyDevice);
mCodecNotifPending = false;
mContext.sendBroadcast(intent, A2dpService.BLUETOOTH_PERM);
}
Log.i(TAG,"mLastDelay: " + mLastDelay + " Current_Delay: " + delay);
if (mIntentBroadcastHandler.hasMessages(MSG_CONNECTION_STATE_CHANGED)) {
Log.i(TAG," Braodcast handler has the pending messages: " );
if (mLastDelay > delay) {
Log.i(TAG,"Last delay is greater than the current delay: " );
delay = mLastDelay;
}
}
mLastDelay = delay;
Log.i(TAG,"connection state change: " + device + " newState: " + newState + " prevState:" + prevState);
mWakeLock.acquire();
// 发送广播
mIntentBroadcastHandler.sendMessageDelayed(mIntentBroadcastHandler.obtainMessage(
MSG_CONNECTION_STATE_CHANGED, prevState, newState, device), delay);
log("Exit broadcastConnectionState() ");
}
broadcastConnectionState中会向AudioManager中设置A2DP的连接状态,返回值用来延时发送广播。AudioManager设置A2DP的连接状态非常重要,这样音频系统根据当前状态,判断音频从哪里发出(蓝牙a2dp、扬声器、耳机)。
Liu Tao
2019-3-28
Android 8 蓝牙 A2DP流程的更多相关文章
- Android 8 蓝牙 扫描流程
记录android 8 蓝牙扫描设备的流程 src/com/android/settings/bluetooth/BluetoothSettings.java @Override protected ...
- 基于Android 的蓝牙A2DP 功能的实现
摘 要:蓝牙(Bluetooth)技术是一种低成本的无线数据与数字通信的开放性全球规范. Android 是Google 于2007 年11 月5 日宣布的基于Linux平台开源手机操作系统名称,该平 ...
- 【转】Android bluetooth介绍(二): android blueZ蓝牙代码架构及其uart 到rfcomm流程
原文网址:http://blog.sina.com.cn/s/blog_602c72c50102uzoj.html 关键词:蓝牙blueZ UART HCI_UART H4 HCI L2CAP ...
- Android蓝牙A2DP连接实现
代码地址如下:http://www.demodashi.com/demo/14624.html 开发环境: 开发工具:Androidstudio 适配机型:honor8(Android6.0), 坚果 ...
- Android 开发 蓝牙开发
前言 蓝牙开发其实分2个部分,一个是正常蓝牙功能的开发(比如Android蓝牙的互相连接.读取蓝牙列表.文件传输.蓝牙耳机等等).另外一个是BLE蓝牙开发(属于低功耗蓝牙设备,设备大多是血糖仪.蓝牙手 ...
- Android BLE蓝牙详细解读
代码地址如下:http://www.demodashi.com/demo/15062.html 随着物联网时代的到来,越来越多的智能硬件设备开始流行起来,比如智能手环.心率检测仪.以及各式各样的智能家 ...
- Android之SystemUI载入流程和NavigationBar的分析
Android之SystemUI载入流程和NavigationBar的分析 本篇仅仅分析SystemUI的载入过程和SystemUI的当中的一个模块StatusBar的小模块NavigationBar ...
- Android 串口蓝牙通信开发Java版本
Android串口BLE蓝牙通信Java版 0. 导语 Qt on Android 蓝牙通信开发 我们都知道,在物联网中,BLE蓝牙是通信设备的关键设备.在传统的物联网应用中,无线WIFI.蓝牙和Zi ...
- ubuntu16.04连接android手机蓝牙共享网络热点
最近的想要用android手机蓝牙共享wifi网络给ubuntu16.04系统用,查了好多资料,发现网上很少有有用的.自己实践后分享如下. 第一步:手机与电脑配对: 该步骤比较简单,网 ...
随机推荐
- [PA2014]Bohater
[PA2014]Bohater 题目大意: 有\(n(n\le10^5)\)只怪物,你的血量为\(z\).要打败第\(i\)只怪物时,你需要先消耗\(d_i\)点生命值,再恢复\(a_i\)点生命值. ...
- CSS设置文字不能被选中
/*设置文字不能被选中 以下为css样式*/ -webkit-user-select:none; -moz-user-select:none; -ms-user-select:none; us ...
- 通过xml处理sql语句时对小于号与大于号的处理转换
以上方法,很容易使用,直接ss < #{ss} 法二 <![CDATA[>=]]>表示大于等于 变量<![CDATA[ < ]]>#{变量}表示 ...
- css3实现不同的loading
样式1: <html> <head> <style type="text/css"> .loading { position: fixed; t ...
- vim查找格式
使用了VIM这么久,却一直无法牢记一些基本的操作指令.今天查找一个关键字时,想不起来怎么查找“下一个”,于是google之并解决,顺便把有用的都贴过来罢. 查找指令:/xxx 往下查找?xxx 往上 ...
- 从注册表清理 IE10,IE11 用户代理字符串(UserAgent)中的垃圾信息
某一天,我发现我的 IE User Agent 字符串里面竟然含有刷机大师.百度浏览器等许多垃圾,国货流氓见怪不怪了. 微软自家的.NET CLR也占据了一大片,看着也不爽. 决定清理一下,但是却没找 ...
- 前端工程化系列[01]-Bower包管理工具的使用
本文主要介绍前端开发中常用的包管理工具Bower,具体包括Bower的基本情况.安装.使用和常见命令等内容,最后还介绍了依赖树管理的常见方式以及Bower采用的策略并进行了比较. 1.1 关于Bowe ...
- 解析 ViewTreeObserver 源码(下)
继上篇内容,本文介绍 ViewTreeObserver 的使用,以及体会其所涉及的观察者模式,期间会附带回顾一些基础知识.最后,我们简单聊一下 Android 的消息传递,附高清示意图,轻松捋清整个传 ...
- Pipenv和Python虚拟环境
Pipenv & 虚拟环境 本教程将引导您完成安装和使用 Python 包. 它将向您展示如何安装和使用必要的工具,并就最佳做法做出强烈推荐.请记住, Python 用于许多不同的目的.准确地 ...
- Apache Kafka 快速入门
概述 Apache Kafka是一个分布式发布-订阅消息系统和强大的队列,可以处理大量的数据,将消息从一个端点传递到另一个端点.Kafka适合离线和在线消息消费,Kafka消息保存在磁盘上,并在集群内 ...