Bluetooth
android -- 蓝牙 bluetooth (一) 入门
分类: Android的原生应用分析 2013-05-19 21:44 6229人阅读 评论(37) 收藏 举报 bluetooth 4.2.2源码 android目录(?)[+]
前段时间在 网上看了一些关于android蓝牙的文章,发现大部分是基于老版本(4.1以前含4.1)的源码,虽然无碍了解蓝牙的基本原理和工作流程,但对着4.2.2的代码看起来总是有些遗憾。所以针对4.2.2版本代码整理下相关知识,当然蓝牙工作的主干流程是没有变的,上电、加载驱动这些动作少不了的,只是这些功能的实现代码位置变了不少。希望本文可以让大家对android4.2的蓝牙部分代码有一个初步的了解。
正文开始前,先明确代码版本:android jellyBean 4.2.2,后续的蓝牙相关文章同样如此。
另推荐个源码在线阅读网址 /,已经知道的童鞋无视这行吧。
入手一个新的模块或应用,当然首先要知道它都有什么了,与它相关的代码在那里,所以先一起看下蓝牙代码分布吧。
1. 代码分布:
packages/apps/Bluetooth/
看这路径肯定是蓝牙应用方面的代码了,主要是关于蓝牙应用协议的表现代码,包括opp、hfp、hdp、a2dp、pan等等,这些名词后面再解释。
frameworks/base/core/java/android/server/
4.2以后这个目录虽然还有了,但里面代码已经转移到应用层了,就是前面那个目录,所以4.2.2上的蓝牙这里可以忽略。
framework/base/core/java/android/bluetooth
这个目录里的代码更像一个桥梁,里面有供java层使用一些类,也有对应的aidl文件联系C、C++部分的代码,还是挺重要的。
kernel\drivers\bluetoothBluetooth
具体协议实现。包括hci,hid,rfcomm,sco,SDP等协议
kernel\net\bluetooth Linux kernel
对各种接口的Bluetoothdevice的驱动。例如:USB接口,串口等,上面kernel这两个目录有可能看不到的,但一定会有的。
external\bluetooth\bluedroid BlueZ (应用空间协议),官方蓝牙协议栈。
system\bluetoothBluetooth 适配层代码,和framework那个作用类似,是串联framework与blueZ的工具。
大致代码分布就是这些,初步查看后让我们再来看下蓝牙的整体结构。
2.整体结构:
这部分直接上图了,看着直观些。图中把JNI部分虽然在目前4.2的代码中在packages层,这里还是画在Framework层了,说明下希望
不要引起理解的误会。从图上可以感觉到整体流程和以前变化不大,所以流程方面的文章看4.1或更早的应该问题也不大。
PS:上图关于蓝牙协议栈的说明有误,4.2里已经不再是bluez了,在些更正一下,当然协议栈这一部分还是要有的,新的协议栈看下面英文:
Android 4.2 introduces a new Bluetooth stack optimized for use with Android devices. The new Bluetooth stack developed in
collaboration between Google and Broadcom replaces the stack based on BlueZ and provides improved compatibility and reliability.
google和broadcom合作开发了一个新蓝牙协议栈,老版本的兼容性问题在所难免了。在此感谢网友andger032的提醒。
3.常用类和名词解释:
\packages\apps\Settings\src\com\android\settings\bluetooth 目录下
BluetoothEnabler.java 界面上蓝牙开启、关闭的开关就是它了,
BluetoothSettings.java 主界面,用于管理配对和连接设备
LocalBluetoothManager.java 提供了蓝牙API上的简单调用接口,这里只是开始。
CachedBluetoothDevice.java 描述蓝牙设备的类,对BluetoothDevice的再封装
BluetoothPairingDialog.java 那个配对提示的对话框
/packages/apps/Phone/src/com/android/phone/
BluetoothPhoneService.java 在phone的目录肯定和电话相关了,蓝牙接听挂断电话会用到这个
/packages/apps/Bluetooth/src/com/android/bluetooth/btservice/
AdapterService.java 4.2后才有的代码,蓝牙打开、关闭、扫描、配对都会走到这里,其实更准确的说它替代了4.1之前的BluetoothService.java,原来的工作就由这个类来完成了。说到这里不能不说4.2蓝牙的目录变了,在4.1及以前的代码中packages层的代码只有opp协议相关应用的代码,也就是文件传输那部分,而4.2的代码应用层的代码则丰富了许多,按具体的蓝牙应用协议来区别,分为以下文件夹(这里一并对蓝牙一些名词作个简单解释):
a2dp 蓝牙立体声,和蓝牙耳机听歌有关那些,另还有个avrcp--音频/视频远程控制配置文件,是用来听歌时暂停,上下歌曲选择的。
btservice 这个前面AdapterService.java的描述大家应该能猜到一些,关于蓝牙基本操作的目录,一切由此开始。
hdp 蓝牙关于医疗方面的应用 Bluetooth Health Device Profile
hfp 和电话相关,蓝牙接听、挂断电话 Hands-free Profile
hid 人机交互接口,蓝牙鼠标键盘什么的就是这个了
opp 不多解释,以前就有。
pan 描述了两个或更多个 Bluetooth 设备如何构成一个即时网络,和网络有关的还有串行端口功能(SPP),拨号网络功能(DUN)
pbap 电话号码簿访问协议(Phonebook Access Profile)
android 4.2的蓝牙应用层部分代码更丰富了,虽然有些目录还没具体代码,不过说不准哪个版本更新就有了,就像4.0添加了hdp医疗那部分一样。
另外原本在framework的JNI代码也被移到packages/apps/bluetooth当中。
/frameworks/base/core/java/android/bluetooth/目录下
BluetoothA2dp.java A2DP的功能实现
BluetoothAdapter.java 蓝牙action的定义,虚拟设备属性以及操作方法
BluetoothAudioGateway.java 蓝牙语音网关
BluetoothClass.java 蓝牙设备类型的定义
BluetoothDevice.java 蓝牙设备属性
BluetoothDevicePicker.java 定义远程蓝牙设备的特性,比如需要认证,设备类型
BluetoothHeadset.java 定义蓝牙headset功能的属性以及接口
BluetoothInputStream.java 蓝牙流接口的实现(输入流)
BluetoothOutputStream.java 蓝牙流接口的实现(输出流)
BluetoothServerSocket.java 蓝牙socket服务端具备的方法
BluetoothSocket.java 蓝牙socket的封装
BluetoothUuid.java 蓝牙uuid的定义以及uuid的解析
以上java文件在使用具体功能会用到,现在只是简单描述下,至于具体使用在后续文章用到时再给出。同时代码说明部分也就写这些了
对于C、C++部分的代码一方面没看那么多,另一方面根据android JNI的命名习惯,大家找起来也很容易。
4.后续分析:
前面从整体上描述蓝牙的基本知识,落实在具体的代码分析上,我们按几个主线功能来走,蓝牙的开关、搜索配对、蓝牙耳机与电话和文件传输,
这几个也算是蓝牙的常用必备功能了,所以在后续文章中将按着这个顺序来跟一下它们代码调用流程。希望可以让你快速的了解蓝牙,当然如果有失误
写错的地方,欢迎反馈,谢谢。android -- 蓝牙 bluetooth (二) 打开蓝牙
分类: Android的原生应用分析 2013-05-23 23:57 6748人阅读 评论(21) 收藏 举报 android bluetooth 打开蓝牙 BT 4.24.2的蓝牙打开流程这一部分还是有些变化的,从界面上看蓝牙开关就是设置settings里那个switch开关,widget开关当然也可以,起点不同而已,后续的流程是一样的。先来看systemServer.java的代码,蓝牙服务开启的地方,最后一个else分支是我们关心的,前两个是模拟器的一个测试模式的。
[java] view plain copy
- if (SystemProperties.get("ro.kernel.qemu").equals("1")) {
- Slog.i(TAG, "No Bluetooh Service (emulator)");
- } else if (factoryTest == SystemServer.FACTORY_TEST_LOW_LEVEL) {
- Slog.i(TAG, "No Bluetooth Service (factory test)");
- } else {
- Slog.i(TAG, "Bluetooth Manager Service");
- bluetooth = new BluetoothManagerService(context);
- ServiceManager.addService(BluetoothAdapter.BLUETOOTH_MANAGER_SERVICE, bluetooth);
- }
暂且看下bluetoothManagerService的构造方法,代码有点多,我们只看两个地方, loadStoredNameAndAddress()是读取蓝牙打开默认名称的地方,isBluetoothPersistedStateOn()是判断是否已打开蓝牙的,如果已打开,后续操作要执行开启蓝牙的动作,前面那几行注册广播其中就有这个作用。
[java] view plain copy
- BluetoothManagerService(Context context) {
- ...一些变量声明初始化...
- IntentFilter filter = new IntentFilter(Intent.ACTION_BOOT_COMPLETED);
- filter.addAction(BluetoothAdapter.ACTION_LOCAL_NAME_CHANGED);
- filter.addAction(Intent.ACTION_USER_SWITCHED);
- registerForAirplaneMode(filter);
- mContext.registerReceiver(mReceiver, filter);
- loadStoredNameAndAddress();
- if (isBluetoothPersistedStateOn()) {
- mEnableExternal = true;
- }
- }
回到界面开关那个看得着的地方,界面上开关就是BluetoothEnabler.java这个类了,而setBluetoothEnabled()则是具体开关动作。看下代码
[java] view plain copy
- public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
- // Show toast message if Bluetooth is not allowed in airplane mode
- if (isChecked &&
- !WirelessSettings.isRadioAllowed(mContext, Settings.Global.RADIO_BLUETOOTH)) {
- Toast.makeText(mContext, R.string.wifi_in_airplane_mode, Toast.LENGTH_SHORT).show();
- // Reset switch to off
- buttonView.setChecked(false);
- }
- if (mLocalAdapter != null) {
- mLocalAdapter.setBluetoothEnabled(isChecked);
- }
- mSwitch.setEnabled(false);
- }
这里在判断是飞行模式不知道为什么没有return,如果是飞行模式会有提示toast弹出,既然这样源码为什么还要执行下面打开流程呢,也许是个bug?不细究这个了,继续看setBluetoothEnabled()方法做什么了,很明显mLocalAdapter(LocalBluetoothAdapter )只是个过渡,里面的 mAdapter(BluetoothAdapter)才是真正的主角,代码如下:
[java] view plain copy
- public void setBluetoothEnabled(boolean enabled) {
- boolean success = enabled ? mAdapter.enable() : mAdapter.disable();
- if (success) {
- setBluetoothStateInt(enabled
- ? BluetoothAdapter.STATE_TURNING_ON
- : BluetoothAdapter.STATE_TURNING_OFF);
- } else {
- .........
- }
- }
拿到BluetoothAdapter对象,代码也简单看下吧,里面是典型的binder应用。
[java] view plain copy
- public static synchronized BluetoothAdapter getDefaultAdapter() {
- if (sAdapter == null) {
- IBinder b = ServiceManager.getService(BLUETOOTH_MANAGER_SERVICE);
- if (b != null) {
- IBluetoothManager managerService = IBluetoothManager.Stub.asInterface(b);
- sAdapter = new BluetoothAdapter(managerService);
- } else {
- Log.e(TAG, "Bluetooth binder is null");
- }
- }
- return sAdapter;
方法,后面要跳转到新类了,贴出来一起看下,这部分好像不同版本还有些出入,不过核心的启动service是一样的,不影响理解。
[java] view plain copy
- private void handleEnable(boolean persist, boolean quietMode) {
- synchronized(mConnection) {
- if ((mBluetooth == null) && (!mBinding)) {
- //Start bind timeout and bind
- Message timeoutMsg=mHandler.obtainMessage(MESSAGE_TIMEOUT_BIND);
- mHandler.sendMessageDelayed(timeoutMsg,TIMEOUT_BIND_MS);
- mConnection.setGetNameAddressOnly(false);
- Intent i = new Intent(IBluetooth.class.getName());
- if (!mContext.bindService(i, mConnection,Context.BIND_AUTO_CREATE,
- UserHandle.USER_CURRENT)) {
- mHandler.removeMessages(MESSAGE_TIMEOUT_BIND);
- Log.e(TAG, "Fail to bind to: " + IBluetooth.class.getName());
- } else {
- mBinding = true;
- }
- }
这样的信息,那就是去AdapterService里看看,里面一共有三个enable(),跳转关系不复杂,我们直接看最后一个关键的。
[java] view plain copy
- public synchronized boolean enable(boolean quietMode) {
- enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
- "Need BLUETOOTH ADMIN permission");
- if (DBG)debugLog("Enable called with quiet mode status = " + mQuietmode);
- mQuietmode = quietMode;
- Message m =
- mAdapterStateMachine.obtainMessage(AdapterState.USER_TURN_ON);
- mAdapterStateMachine.sendMessage(m);
- return true;
- }
处理在同文件下面的代码里
[java] view plain copy
- case STARTED: {
- if (DBG) Log.d(TAG,"CURRENT_STATE=PENDING, MESSAGE = STARTED, isTurningOn=" + isTurningOn + ", isTurningOff=" + isTurningOff);
- //Remove start timeout
- removeMessages(START_TIMEOUT);
- //Enable
- boolean ret = mAdapterService.enableNative();
- if (!ret) {
- Log.e(TAG, "Error while turning Bluetooth On");
- notifyAdapterStateChange(BluetoothAdapter.STATE_OFF);
- transitionTo(mOffState);
- } else {
- sendMessageDelayed(ENABLE_TIMEOUT, ENABLE_TIMEOUT_DELAY);
- }
看到那个enableNative()函数调用了吧,又要用到JNI了,稍微回头看下前面的代码,我们先从应用界面开关BluetoothEnabler走到framework的BluetoothAdapter,又回到package的adapterService,现在又要去JNI的C++代码了,往常一般是packages -->framework-->下面一层,这次顺序有些颠倒了,不过这不能影响我们跟踪代码,最后
还是要到下面去的。一起往下看吧。
根据android JNI的函数命名惯例很容易找到enableNative对应的C++函数在packages/apps/Bluetooth/jni/com_android_bluetooth_btservice_AdapterService.cpp里面
[java] view plain copy
- static jboolean enableNative(JNIEnv* env, jobject obj) {
- ALOGV("%s:",__FUNCTION__);
- jboolean result = JNI_FALSE;
- if (!sBluetoothInterface) return result;
- int ret = sBluetoothInterface->enable();
- result = (ret == BT_STATUS_SUCCESS) ? JNI_TRUE : JNI_FALSE;
- return result;
- }
代码瞬间简洁了不少,看来更多的故事还在下面,sBluetoothInterface这是什么,直接关系到下一步去哪的问题,看下变量声明,原来是
Const bt_interface_t *sBluetoothInterface = NULL; 再去找在哪初始化,搜索external目录可以找到/external/bluetooth/bluedroid/btif/src/bluetooth.c
[cpp] view plain copy
- static const bt_interface_t bluetoothInterface = {
- sizeof(bt_interface_t),
- init,
- enable,
- disable,
- .............
- start_discovery,
- cancel_discovery,
- create_bond,
- remove_bond,
- cancel_bond,
- ...............
- };
[cpp] view plain copy
- static int enable( void )
- {
- ALOGI("enable");
- /* sanity check */
- if (interface_ready() == FALSE)
- return BT_STATUS_NOT_READY;
- return btif_enable_bluetooth();
- }
[cpp] view plain copy
- bt_status_t btif_enable_bluetooth(void)
- {
- BTIF_TRACE_DEBUG0("BTIF ENABLE BLUETOOTH");
- if (btif_core_state != BTIF_CORE_STATE_DISABLED)
- {
- ALOGD("not disabled\n");
- return BT_STATUS_DONE;
- }
- btif_core_state = BTIF_CORE_STATE_ENABLING;
- /* Create the GKI tasks and run them */
- bte_main_enable(btif_local_bd_addr.address);
- return BT_STATUS_SUCCESS;
- }
[cpp] view plain copy
- void bte_main_enable(uint8_t *local_addr)
- {
- APPL_TRACE_DEBUG1("%s", __FUNCTION__);
- ........................
- #if (defined (BT_CLEAN_TURN_ON_DISABLED) && BT_CLEAN_TURN_ON_DISABLED == TRUE)
- APPL_TRACE_DEBUG1("%s Not Turninig Off the BT before Turninig ON", __FUNCTION__);
- #else
- /* toggle chip power to ensure we will reset chip in case
- a previous stack shutdown wasn't completed gracefully */
- bt_hc_if->set_power(BT_HC_CHIP_PWR_OFF);
- #endif
- bt_hc_if->set_power(BT_HC_CHIP_PWR_ON);
- bt_hc_if->preload(NULL);
- }
- .............................
- }
路径在这里/external/bluetooth/bluedroid/hci/src/bt_hci_bdroid.c,看看set_power里面有什么,快到头了
[cpp] view plain copy
- static void set_power(bt_hc_chip_power_state_t state)
- {
- int pwr_state;
- BTHCDBG("set_power %d", state);
- /* Calling vendor-specific part */
- pwr_state = (state == BT_HC_CHIP_PWR_ON) ? BT_VND_PWR_ON : BT_VND_PWR_OFF;
- if (bt_vnd_if)
- bt_vnd_if->op(BT_VND_OP_POWER_CTRL, &pwr_state);
- else
- ALOGE("vendor lib is missing!");
- }
[cpp] view plain copy
- /* Entry point of DLib --
- * Vendor library needs to implement the body of bt_vendor_interface_t
- * structure and uses the below name as the variable name. HCI library
- * will use this symbol name to get address of the object through the
- * dlsym call.
- */
- extern const bt_vendor_interface_t BLUETOOTH_VENDOR_LIB_INTERFACE;
- bt_vendor_interface_t *bt_vnd_if=NULL;
google定义好了接口,具体实现要看vendor厂商来做了,这后面怎么实现就看各家芯片商怎么写了,肯定各有不同,而且这一部分代码一般是不会公开,当然授权购买后除外了。所以在4.2的源码中我们只跟到这里了,那后面会做什么呢,加载驱动和上电这两项肯定要有了,打开蓝牙没这两步怎么行,类似下面的字符串
[cpp] view plain copy
- static const char* BT_DRIVER_MODULE_PATH = "/system/lib/modules/mbt8xxx.ko";
- static const char* BT_DRIVER_MODULE_NAME = "bt8xxx";
- static const char* BT_DRIVER_MODULE_INIT_ARG = " init_cfg=";
- static const char* BT_DRIVER_MODULE_INIT_CFG_PATH = "bt_init_cfg.conf";
在有类似下面的动作,insmod加载驱动,rfkill控制上下电,具体厂商具体做法也不同。
[cpp] view plain copy
- ret = insmod(BT_DRIVER_MODULE_PATH, arg_buf);
- ret = system("/system/bin/rfkill block all");
android -- 蓝牙 bluetooth (三)搜索蓝牙
分类: Android的原生应用分析 2013-05-31 22:03 3100人阅读 评论(8) 收藏 举报 bluetooth 蓝牙 startScanning Android 4.2源码
接上篇打开蓝牙继续,来一起看下蓝牙搜索的流程,触发蓝牙搜索的条件形式上有两种,一是在蓝牙设置界面开启蓝牙会直接开始搜索,另一个是先打开蓝牙开关在进入蓝牙设置界面也会触发搜索,也可能还有其它触发方式,但最后都要来到BluetoothSettngs.java的startScanning(),我们分析的起点也从这里开始,起步代码如下
[cpp] view plain copy
- private void updateContent(int bluetoothState, boolean scanState) {
- if (numberOfPairedDevices == 0) {
- preferenceScreen.removePreference(mPairedDevicesCategory);
- if (scanState == true) {
- mActivityStarted = false;
- startScanning();
- } else<span style="font-family: Arial, Helvetica, sans-serif;"> ........</span>
- }
- private void startScanning() {
- if (!mAvailableDevicesCategoryIsPresent) {
- getPreferenceScreen().addPreference(mAvailableDevicesCategory);
- }
- mLocalAdapter.startScanning(true);
- }
其实在这里蓝牙搜索和打开流程是结构上是一致的,利用LocalBluetoothAdapter.java过渡到BluetoothAdapter.java再跳转至AdapterService.java要稍微留意下的是在这个过渡中startScaning()方法变成了startDiscovery()方法,看下代码:packages/apps/Settings/src/com/android/settings/bluetooth/LocalBluetoothAdapter.java
[java] view plain copy
- void startScanning(boolean force) {
- if (!mAdapter.isDiscovering()) {
- if (!force) {
- // Don't scan more than frequently than SCAN_EXPIRATION_MS,
- // unless forced
- if (mLastScan + SCAN_EXPIRATION_MS > System.currentTimeMillis()) {
- return;
- }
- // If we are playing music, don't scan unless forced.
- A2dpProfile a2dp = mProfileManager.getA2dpProfile();
- if (a2dp != null && a2dp.isA2dpPlaying()) {
- return;
- }
- }
- //这里才是我们最关注的,前面限制条件关注一下就行了
- if (mAdapter.startDiscovery()) {
- mLastScan = System.currentTimeMillis();
- }
- }
[java] view plain copy
- public boolean startDiscovery() {
- .............................
- AdapterService service = getService();
- if (service == null) return false;
- return service.startDiscovery();
- }
下面的代码路径自然是 :packages/apps/Bluetooth/src/com/android/bluetooth/btservice/AdapterService.java,
[java] view plain copy
- boolean startDiscovery() {
- enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
- "Need BLUETOOTH ADMIN permission");
- return startDiscoveryNative();
- }
和打开蓝牙根本就是一个套路,上面的流程略过一小步,很简单的不写了,下面要怎么走,估计大家也都猜到了,JNI应该出场了,
路径:/packages/apps/Bluetooth/jni/com_android_bluetooth_btservice_AdapterService.cpp
[cpp] view plain copy
- static jboolean startDiscoveryNative(JNIEnv* env, jobject obj) {
- ALOGV("%s:",__FUNCTION__);
- jboolean result = JNI_FALSE;
- if (!sBluetoothInterface) return result;
- int ret = sBluetoothInterface->start_discovery();
- result = (ret == BT_STATUS_SUCCESS) ? JNI_TRUE : JNI_FALSE;
- return result;
- }
在下面要去哪?稍微要动下脑筋,不过我们在上一篇android -- 蓝牙 bluetooth (二) 打开蓝牙已经说过怎么找了,注意android.mk文件,先找头文件,再找对应的实现C文件代码。就是现在回顾下,蓝牙打开和搜索的代码流程我们都看了,跳转都是一个套路,settings界面发起,LocalBluetoothAdapter.java过渡,去framework的转转(BluetoothAdapter.java)后回到packages的AdapterService.java,再走JNI来的external。流程就是这样的,相信类似的功能跳转(比如蓝牙配对,关闭蓝牙,停止扫描这些)大家都应该熟悉了,后面再有类似的功能就写函数名一笔带过了,还有这里要注意的就是这个start_discovery()实现代码的寻找,留意mk文件就是了,不复杂。小结结束,继续看代码 路径:/external/bluetooth/bluedroid/btif/src/bluetooth.c
[cpp] view plain copy
- static int start_discovery(void)
- {
- /* sanity check */
- if (interface_ready() == FALSE)
- return BT_STATUS_NOT_READY;
- return btif_dm_start_discovery();
- }
下面代码直接跳转就可以找到,路径external/bluetooth/bluedroid/btif/src/btif_dm.c
这个代码有点多,不过里面的信息也很多,所以连注释也一起保留的贴出来了,蓝牙的搜索实现并没有像蓝牙打开那样交由vendor厂商实现,在这里已经写出来了,仔细看下那些#if和#else,都是一些查询条件的调置,#if (BLE_INCLUDED == TRUE) 这个应该就google为蓝牙4.0 LE作的准备了,也算是今年google I/O大会上宣布即将支持蓝牙4.0低能耗版一个佐证吧,对于代码里面那些字符串的含义看这里好了external/bluetooth/bluedroid/bta/include/bta_api.h,一个头文件,大部分字符串和结构体的定义都在这了,多少还有些注释。
[cpp] view plain copy
- bt_status_t btif_dm_start_discovery(void)
- {
- tBTA_DM_INQ inq_params;
- tBTA_SERVICE_MASK services = 0;
- BTIF_TRACE_EVENT1("%s", __FUNCTION__);
- /* TODO: Do we need to handle multiple inquiries at the same time? */
- /* Set inquiry params and call API */
- #if (BLE_INCLUDED == TRUE)
- inq_params.mode = BTA_DM_GENERAL_INQUIRY|BTA_BLE_GENERAL_INQUIRY;
- #else
- inq_params.mode = BTA_DM_GENERAL_INQUIRY;
- #endif
- inq_params.duration = BTIF_DM_DEFAULT_INQ_MAX_DURATION;
- inq_params.max_resps = BTIF_DM_DEFAULT_INQ_MAX_RESULTS;
- inq_params.report_dup = TRUE;
- inq_params.filter_type = BTA_DM_INQ_CLR;
- /* TODO: Filter device by BDA needs to be implemented here */
- /* Will be enabled to TRUE once inquiry busy level has been received */
- btif_dm_inquiry_in_progress = FALSE;
- /* find nearby devices */
- BTA_DmSearch(&inq_params, services, bte_search_devices_evt);
- return BT_STATUS_SUCCESS;
- }
BTA_DmSearch()方法是看起来是要搜索了,不过里面这个家伙bte_search_devices_evt才是真正干活的主力,所以我们先看它,在这个函数里
[cpp] view plain copy
- static void bte_search_devices_evt(tBTA_DM_SEARCH_EVT event, tBTA_DM_SEARCH *p_data) {
- UINT16 param_len = 0;
- if (p_data)
- param_len += sizeof(tBTA_DM_SEARCH);
- /* Allocate buffer to hold the pointers (deep copy). The pointers will point to the end of the tBTA_DM_SEARCH */
- switch (event)
- {
- case BTA_DM_INQ_RES_EVT:
- {
- if (p_data->inq_res.p_eir)
- param_len += HCI_EXT_INQ_RESPONSE_LEN;
- }
- break;
- ..............................
- }
- BTIF_TRACE_DEBUG3("%s event=%s param_len=%d", __FUNCTION__, dump_dm_search_event(event), param_len);
- /* if remote name is available in EIR, set teh flag so that stack doesnt trigger RNR */
- if (event == BTA_DM_INQ_RES_EVT)
- p_data->inq_res.remt_name_not_required = check_eir_remote_name(p_data, NULL, NULL);
- btif_transfer_context (btif_dm_search_devices_evt , (UINT16) event, (void *)p_data, param_len,
- (param_len > sizeof(tBTA_DM_SEARCH)) ? search_devices_copy_cb : NULL);
- }
- static void btif_dm_search_devices_evt (UINT16 event, char *p_param)
- tBTA_DM_SEARCH *p_search_data;
- BTIF_TRACE_EVENT2("%s event=%s", __FUNCTION__, dump_dm_search_event(event));
- switch (event)
- {
- case BTA_DM_DISC_RES_EVT:
- {
- p_search_data = (tBTA_DM_SEARCH *)p_param;
- /* Remote name update */
- if (strlen((const char *) p_search_data->disc_res.bd_name))
- {
- bt_property_t properties[1];
- bt_bdaddr_t bdaddr;
- bt_status_t status;
- properties[0].type = BT_PROPERTY_BDNAME;
- properties[0].val = p_search_data->disc_res.bd_name;
- properties[0].len = strlen((char *)p_search_data->disc_res.bd_name);
- bdcpy(bdaddr.address, p_search_data->disc_res.bd_addr);
- status = btif_storage_set_remote_device_property(&bdaddr, &properties[0]);
- ASSERTC(status == BT_STATUS_SUCCESS, "failed to save remote device property", status);
- HAL_CBACK(bt_hal_cbacks, remote_device_properties_cb,
- status, &bdaddr, 1, properties);
- }
- /* TODO: Services? */
- }
- break;
05-30 13:52:14.890 1578 2612 D bt-btif : search_devices_copy_cb: event=BTA_DM_INQ_RES_EVT
05-30 13:52:14.890 1578 2584 I bt-btif : btif_dm_search_devices_evt event=BTA_DM_INQ_RES_EVT
05-30 13:52:14.890 1578 2584 D bt-btif : btif_dm_search_devices_evt() ec:89:f5:ba:fb:03 device_type = 0x1
当然回过头我们还要看下那个BTA_DmSearch(),看它的实现,更应该是起消息发送的作用,代码在/external/bluetooth/bluedroid/bta/dm/bta_dm_api.c,这个函数具体流程并没有看多少,当工具方法看了,有时间看看还是没坏处的。 [cpp] view plain copy
- void BTA_DmSearch(tBTA_DM_INQ *p_dm_inq, tBTA_SERVICE_MASK services, tBTA_DM_SEARCH_CBACK *p_cback)
- { tBTA_DM_API_SEARCH *p_msg;
- if ((p_msg = (tBTA_DM_API_SEARCH *) GKI_getbuf(sizeof(tBTA_DM_API_SEARCH))) != NULL)
- {
- memset(p_msg, 0, sizeof(tBTA_DM_API_SEARCH));
- p_msg->hdr.event = BTA_DM_API_SEARCH_EVT;
- memcpy(&p_msg->inq_params, p_dm_inq, sizeof(tBTA_DM_INQ));
- p_msg->services = services;
- p_msg->p_cback = p_cback;
- p_msg->rs_res = BTA_DM_RS_NONE;
- bta_sys_sendmsg(p_msg);
- }
- }
- method_deviceFoundCallback = env->GetMethodID(jniCallbackClass, "deviceFoundCallback", "([B)V");
- void deviceFoundCallback(byte[] address) {
- // The device properties are already registered - we can send the intent
- // now
- BluetoothDevice device = getDevice(address);
- debugLog("deviceFoundCallback: Remote Address is:" + device);
- DeviceProperties deviceProp = getDeviceProperties(device);
- if (deviceProp == null) {
- errorLog("Device Properties is null for Device:" + device);
- return;
- }
- Intent intent = new Intent(BluetoothDevice.ACTION_FOUND);
- intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device);
- intent.putExtra(BluetoothDevice.EXTRA_CLASS,
- new BluetoothClass(Integer.valueOf(deviceProp.mBluetoothClass)));
- intent.putExtra(BluetoothDevice.EXTRA_RSSI, deviceProp.mRssi);
- intent.putExtra(BluetoothDevice.EXTRA_NAME, deviceProp.mName);
- mAdapterService.sendBroadcast(intent, mAdapterService.BLUETOOTH_PERM);
- }
- addHandler(BluetoothDevice.ACTION_FOUND, new DeviceFoundHandler());
- private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
- @Override
- public void onReceive(Context context, Intent intent) {
- String action = intent.getAction();
- BluetoothDevice device = intent
- .getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
- Handler handler = mHandlerMap.get(action);
- if (handler != null) {
- handler.onReceive(context, intent, device);
- }
- }
- };
- private class DeviceFoundHandler implements Handler {
- public void onReceive(Context context, Intent intent,
- BluetoothDevice device) {
- ........................
- // TODO Pick up UUID. They should be available for 2.1 devices.
- // Skip for now, there's a bluez problem and we are not getting uuids even for 2.1.
- CachedBluetoothDevice cachedDevice = mDeviceManager.findDevice(device);
- if (cachedDevice == null) {
- cachedDevice = mDeviceManager.addDevice(mLocalAdapter, mProfileManager, device);
- Log.d(TAG, "DeviceFoundHandler created new CachedBluetoothDevice: "
- + cachedDevice);
- // callback to UI to create Preference for new device
- dispatchDeviceAdded(cachedDevice);
- }
- ......................
- }
- }
- public void onDeviceAdded(CachedBluetoothDevice cachedDevice) {
- if (mDevicePreferenceMap.get(cachedDevice) != null) {
- return;
- }
- // Prevent updates while the list shows one of the state messages
- if (mLocalAdapter.getBluetoothState() != BluetoothAdapter.STATE_ON) return;
- if (mFilter.matches(cachedDevice.getDevice())) {
- createDevicePreference(cachedDevice);
- }
- }
- void createDevicePreference(CachedBluetoothDevice cachedDevice) {
- BluetoothDevicePreference preference = new BluetoothDevicePreference(
- getActivity(), cachedDevice);
- initDevicePreference(preference);
- mDeviceListGroup.addPreference(preference);
- mDevicePreferenceMap.put(cachedDevice, preference);
- }
到目前为止,包括前面的打开流程分析,还仅是针对代码流程做的分析,对于蓝牙协议方面东西还没有涉及,比如蓝牙是如何发现其它蓝牙设备,这个流程究竟是怎么工作还不是很清楚,后续会尽量关注这些问题,估计看起来就没那么容易,欢迎有经验的朋友指点一二,当然对于本文不足,欢迎拍砖讨论。分享是快乐的,谢谢!
--------------modify 2013.6.2 21:08--------
更新蓝牙搜索返回后的跳转代码android -- 蓝牙 bluetooth (四)OPP文件传输
分类: Android的原生应用分析 2013-06-22 21:51 3865人阅读 评论(21) 收藏 举报 4.2源码 ANDROID BT opp 蓝牙在前面android -- 蓝牙 bluetooth (一) 入门文章结尾中提到了会按四个方面来写这系列的文章,前面已写了蓝牙打开和蓝牙搜索,这次一起来看下蓝牙文件分享的流程,也就是蓝牙应用opp目录下的代码,作为蓝牙最基本的一个功能,这部分的代码在之前的版本中就已经有了,新旧版本代码对比很多类名都是一样的,这一部分新东西不多,写在这里帮助大家梳理下流程吧。
有没有这种感觉,智能手机的普及让我们提高了一点对蓝牙的关注,手机间使用蓝牙互传文件应该是最常用的应用之一,手机与电脑也可以通过蓝牙做同样的事情,大部分笔记本都支持蓝牙功能,本本上蓝牙芯片多数是broadcom的,也有其它厂商(比如东芝)不过数量不多,毕竟broadcom在BT这方面是老大。不过本本上蓝牙一般只支持蓝牙耳机听歌,并没实现对opp的支持,如果体验下手机与电脑的蓝牙文件传输怎么办呢,安装一个叫bluesoleil(中文名好像是千月)软件就可以了,这个软件对蓝牙功能的支持还是比较全的。可能需要卸载本本自带蓝牙驱动。扯淡结束,本文还是要关注手机间蓝牙opp的代码流程,这段的废话也许能帮助你提高下对蓝牙的体验。
蓝牙发送文件时发送端先来到这里packages/apps/Bluetooth/src/com/android/bluetooth/opp/BluetoothOppLauncherActivity.java,一个没有界面只是提取下文件信息的中转站,源码的注释写的很清楚了,两个分支action.equals(Intent.ACTION_SEND)和action.equals(Intent.ACTION_SEND_MULTIPLE)
[java] view plain copy
- if (action.equals(Intent.ACTION_SEND) || action.equals(Intent.ACTION_SEND_MULTIPLE)) {
- //Check if Bluetooth is available in the beginning instead of at the end
- if (!isBluetoothAllowed()) {
- Intent in = new Intent(this, BluetoothOppBtErrorActivity.class);
- in.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
- in.putExtra("title", this.getString(R.string.airplane_error_title));
- in.putExtra("content", this.getString(R.string.airplane_error_msg));
- startActivity(in);
- finish();
- return;
- }
- if (action.equals(Intent.ACTION_SEND)) {
- .......
- Thread t = new Thread(new Runnable() {
- public void run() {
- BluetoothOppManager.getInstance(BluetoothOppLauncherActivity.this)
- .saveSendingFileInfo(type,fileUri.toString(), false);
- //Done getting file info..Launch device picker
- //and finish this activity
- launchDevicePicker();
- finish();
- }
- }); ......
- } else if (action.equals(Intent.ACTION_SEND_MULTIPLE)) {
- .......
- }
最前面那个isBluetoothAllowed()会判断是否处于飞行模式,如果是会禁止发送的。在launchDevicePicker()里还会判断蓝牙是否已经打开,就是下面这个条件语句(!BluetoothOppManager.getInstance(this).isEnabled())。如果已经打开了蓝牙,如果蓝牙打开了就进入设备选择界面DeviceListPreferenceFragment(DevicePickerFragment)选择设备,这个跳转过程简单说明下,注意这个new Intent(BluetoothDevicePicker.ACTION_LAUNCH)里字符串,完整定义public static final String ACTION_LAUNCH = "android.bluetooth.devicepicker.action.LAUNCH";路径frameworks/base/core/java/android/bluetooth/BluetoothDevicePicker.java,你会在setting应用的manifest.xml里发现
[html] view plain copy- <activity android:name=".bluetooth.DevicePickerActivity"
- android:theme="@android:style/Theme.Holo.DialogWhenLarge"
- android:label="@string/device_picker"
- android:clearTaskOnLaunch="true">
- <intent-filter>
- <action android:name="android.bluetooth.devicepicker.action.LAUNCH" />
- <category android:name="android.intent.category.DEFAULT" />
- </intent-filter>
- </activity>
[html] view plain copy
- <fragment
- android:id="@+id/bluetooth_device_picker_fragment"
- android:name="com.android.settings.bluetooth.DevicePickerFragment"
- android:layout_width="match_parent"
- android:layout_height="0dip"
- android:layout_weight="1" />
[java] view plain copy
- void onDevicePreferenceClick(BluetoothDevicePreference btPreference) {
- mLocalAdapter.stopScanning();
- LocalBluetoothPreferences.persistSelectedDeviceInPicker(
- getActivity(), mSelectedDevice.getAddress());
- if ((btPreference.getCachedDevice().getBondState() ==
- BluetoothDevice.BOND_BONDED) || !mNeedAuth) {
- sendDevicePickedIntent(mSelectedDevice);
- finish();
- } else {
- super.onDevicePreferenceClick(btPreference);
- }
- }<div> public static final String ACTION_LAUNCH = "android.bluetooth.devicepicker.action.LAUNCH";
- private void sendDevicePickedIntent(BluetoothDevice device) {
- Intent intent = new Intent(BluetoothDevicePicker.ACTION_DEVICE_SELECTED);
- intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device);
- if (mLaunchPackage != null && mLaunchClass != null) {
- intent.setClassName(mLaunchPackage, mLaunchClass);
- }
- getActivity().sendBroadcast(intent);}
- </div>
[java] view plain copy
- else if (action.equals(BluetoothDevicePicker.ACTION_DEVICE_SELECTED)) {
- BluetoothOppManager mOppManager = BluetoothOppManager.getInstance(context);
- BluetoothDevice remoteDevice = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
- // Insert transfer session record to database
- mOppManager.startTransfer(remoteDevice);
- // Display toast message
- String deviceName = mOppManager.getDeviceName(remoteDevice);
- .......
- }
[java] view plain copy
- public void startTransfer(BluetoothDevice device) {
- if (V) Log.v(TAG, "Active InsertShareThread number is : " + mInsertShareThreadNum);
- InsertShareInfoThread insertThread;
- synchronized (BluetoothOppManager.this) {
- if (mInsertShareThreadNum > ALLOWED_INSERT_SHARE_THREAD_NUMBER) {
- ...........
- return;
- }
- insertThread = new InsertShareInfoThread(device, mMultipleFlag, mMimeTypeOfSendingFile,
- mUriOfSendingFile, mMimeTypeOfSendingFiles, mUrisOfSendingFiles,
- mIsHandoverInitiated);
- if (mMultipleFlag) {
- mfileNumInBatch = mUrisOfSendingFiles.size();
- }
- }
- insertThread.start();
- }
- public void run() {
- Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
- ..........
- if (mIsMultiple) {
- insertMultipleShare();
- } else {
- insertSingleShare();
- }
- .......... }
以insertSingleShare() 为例,在它的实现会看到mContext.getContentResolver().insert,不多想了,要去provider里找到insert()函数了,
对应的代码在BluetoothOppProvider.java (bluetooth\src\com\android\bluetooth\opp),insert的函数实现如下,里面又拉起BluetoothOppService,开始还以为只是针对数据库的操作,差点错过了风景。路径/packages/apps/Bluetooth/src/com/android/bluetooth/opp/BluetoothOppService.java
[java] view plain copy
- public Uri insert(Uri uri, ContentValues values) {
- if (rowID != -1) {
- context.startService(new Intent(context, BluetoothOppService.class));
- ret = Uri.parse(BluetoothShare.CONTENT_URI + "/" + rowID);
- context.getContentResolver().notifyChange(uri, null);
- } else {
- if (D) Log.d(TAG, "couldn't insert into btopp database");
- }
在BluetoothOppService的onStartCommand方法中会看到updateFromProvider(),这里又开启了一个线程UpdateThread,后续代码当然是看它的run方法了,这里面内容不少,好在这部分代码注释比较多,理解起来不难。先暂时只关心发送的动作insertShare方法,代码也不少,只贴出了告诉我们接下来去哪里的代码和有关的逻辑注释,在下面的代码我们可以看到 BluetoothOppTransfer.java的对象,下一站就是它了。
[java] view plain copy
- private void insertShare(Cursor cursor, int arrayPos) {
- .........
- /*
- * Add info into a batch. The logic is
- * 1) Only add valid and readyToStart info
- * 2) If there is no batch, create a batch and insert this transfer into batch,
- * then run the batch
- * 3) If there is existing batch and timestamp match, insert transfer into batch
- * 4) If there is existing batch and timestamp does not match, create a new batch and
- * put in queue
- */
- if (info.isReadyToStart()) {
- .............
- if (mBatchs.size() == 0) {
- ........
- mBatchs.add(newBatch);
- if (info.mDirection == BluetoothShare.DIRECTION_OUTBOUND) {
- mTransfer = new BluetoothOppTransfer(this, mPowerManager, newBatch);
- } else if (info.mDirection == BluetoothShare.DIRECTION_INBOUND) {
- mServerTransfer = new BluetoothOppTransfer(this, mPowerManager, newBatch,
- mServerSession);
- }
- if (info.mDirection == BluetoothShare.DIRECTION_OUTBOUND && mTransfer != null) {
- mTransfer.start();
- } else if (info.mDirection == BluetoothShare.DIRECTION_INBOUND
- && mServerTransfer != null) {
- mServerTransfer.start();
- }
- } else {
- .........
- }}
[java] view plain copy
- public void start() {
- ....这里省略未贴的代码是检查蓝牙是否打开,一个很谨慎的判断。看似无用,不过还是安全第一。
- if (mHandlerThread == null) {
- ........
- if (mBatch.mDirection == BluetoothShare.DIRECTION_OUTBOUND) {
- /* for outbound transfer, we do connect first */
- startConnectSession();
- } else if (mBatch.mDirection == BluetoothShare.DIRECTION_INBOUND) {
- /*
- * for inbound transfer, it's already connected, so we start
- * OBEX session directly
- */
- startObexSession();
- }
- }
- }
上面的代码是分发送文件和接收文件的,看下这两行代码就很清楚了,如果分享给别人是OUTBOUND,先执行startConnectSession(),这个函数最后还是要跑到startObexSession()这里的,如果收文件直接startObexSession,所以后面就只看startObexSession方法了
[java] view plain copy
- // This transfer is outbound, e.g. share file to other device.
- public static final int DIRECTION_OUTBOUND = 0;
- // This transfer is inbound, e.g. receive file from other device.
- public static final int DIRECTION_INBOUND = 1;
[java] view plain copy
- private void startObexSession() {
- if (mBatch.mDirection == BluetoothShare.DIRECTION_OUTBOUND) {
- if (V) Log.v(TAG, "Create Client session with transport " + mTransport.toString());
- mSession = new BluetoothOppObexClientSession(mContext, mTransport);
- } else if (mBatch.mDirection == BluetoothShare.DIRECTION_INBOUND) {
- if (mSession == null) {
- markBatchFailed();
- mBatch.mStatus = Constants.BATCH_STATUS_FAILED;
- return;
- }
- if (V) Log.v(TAG, "Transfer has Server session" + mSession.toString());
- }
- mSession.start(mSessionHandler);
- processCurrentShare();
- }
[java] view plain copy
- private void doSend() {
- int status = BluetoothShare.STATUS_SUCCESS;
- ........关于status值的判断
- if (status == BluetoothShare.STATUS_SUCCESS) {
- /* do real send */ //看到这个注释了没,它才是真家伙sendFile
- if (mFileInfo.mFileName != null) {
- status = sendFile(mFileInfo);
- } else {
- /* this is invalid request */
- status = mFileInfo.mStatus;
- }
- waitingForShare = true;
- } else {
- Constants.updateShareStatus(mContext1, mInfo.mId, status);
- }
- if (status == BluetoothShare.STATUS_SUCCESS) {
- Message msg = Message.obtain(mCallback);
- msg.what = BluetoothOppObexSession.MSG_SHARE_COMPLETE;
- msg.obj = mInfo;
- msg.sendToTarget();
- } else {
- Message msg = Message.obtain(mCallback);
- msg.what = BluetoothOppObexSession.MSG_SESSION_ERROR;
- mInfo.mStatus = status;
- msg.obj = mInfo;
- msg.sendToTarget();
- }
- }
代码路径在frameworks/base/obex/javax/obex/HeaderSet.java。这个sendFile方法行数虽然多,不过逻辑还是比较清晰的,在这里就不贴了。到这蓝牙发送文件流程也就此结束。由于发送文件时长肯定是不确定,所以在这个流程我们看到了很多开启线程代码也是很正常的,对于这线程,直接看对应的run方法就是了。
对于蓝牙接收文件时会收到MSG_INCOMING_BTOPP_CONNECTION消息,收到这个消息是由于在蓝牙打开,即蓝牙状态是 BluetoothAdapter.STATE_ON时会执行
startSocketListener(),在这个函数开启了监听程序,看下面贴在一起的代码就明白了,
[java] view plain copy
- if (action.equals(BluetoothAdapter.ACTION_STATE_CHANGED)) {
- switch (intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, BluetoothAdapter.ERROR)) {
- case BluetoothAdapter.STATE_ON:
- if (V) Log.v(TAG,"Receiver BLUETOOTH_STATE_CHANGED_ACTION, BLUETOOTH_STATE_ON");
- startSocketListener();
- break;
- private void startSocketListener() {
- if (V) Log.v(TAG, "start RfcommListener");
- mSocketListener.start(mHandler);
- if (V) Log.v(TAG, "RfcommListener started");
- }
- mSocketListener.start(mHandler);这个的实现在这里,比较长,没有贴上来
- /packages/apps/Bluetooth/src/com/android/bluetooth/opp/BluetoothOppRfcommListener.java
[java] view plain copy
- private void createServerSession(ObexTransport transport) {
- mServerSession = new BluetoothOppObexServerSession(this, transport);
- mServerSession.preStart();
- }
android -- 蓝牙 bluetooth (五)接电话与听音乐
分类: Android的原生应用分析 2013-07-13 20:53 3399人阅读 评论(12) 收藏 举报 蓝牙 android bluetooth 4.2源码 HFP目录(?)[+]
前段时间似乎所有的事情都赶在一起,回家、集体出游、出差,折腾了近一个月,终于算暂时清静了,但清静只是暂时,估计马上又要出差了,所以赶紧把蓝牙这一部分的文章了结下,按之前提到的目录,本文是关于蓝牙接打电话和听音乐的流程分析,对应蓝牙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(),只看连接部分好了,注意下面代码里的注释, [java] view plain copy- /* package */ void connectBluetoothAudio() {
- if (VDBG) log("connectBluetoothAudio()...");
- if (mBluetoothHeadset != null) {
- // TODO(BT) check return
- mBluetoothHeadset.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();
- public boolean connectAudio() {
- HeadsetService service = getService();
- if (service == null) return false;
- return service.connectAudio();
- }
- 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;
- }
- 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;
- }
- 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的连接过程,在蓝牙搜索结果列表连接一个蓝牙耳机,既然是从设备列表开始,所以起步代码自然是这个了 [java] view plain copy- 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);//没别的了,只能看到这里
- }
[java] view plain copy
- // 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);//连接在这里,
- }
- }
- }
- .............
- 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);
- }
- 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);
- }
- }
- 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 -- 蓝牙 bluetooth (二) 打开蓝牙
android -- 蓝牙 bluetooth (三)搜索蓝牙 android -- 蓝牙 bluetooth (四)OPP文件传输 最后感谢在前面文章中网友的热心回复与纠正,学习路上一起分享是快乐的。谢谢!