【转】android 蓝牙

前段时间在 网上看了一些关于android蓝牙的文章,发现大部分是基于老版本(4.1以前含4.1)的源码,虽然无碍了解蓝牙的基本原理和工作流程,但对着4.2.2的代码看起来总是有些遗憾。所以针对4.2.2版本代码整理下相关知识,当然蓝牙工作的主干流程是没有变的,上电、加载驱动这些动作少不了的,只是这些功能的实现代码位置变了不少。希望本文可以让大家对android4.2的蓝牙部分代码有一个初步的了解。

        正文开始前,先明确代码版本:android  jellyBean 4.2.2,后续的蓝牙相关文章同样如此。

        另推荐个源码在线阅读网址 http://androidxref.com/,已经知道的童鞋无视这行吧。

入手一个新的模块或应用,当然首先要知道它都有什么了,与它相关的代码在那里,所以先一起看下蓝牙代码分布吧。

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.后续分析:

前面从整体上描述蓝牙的基本知识,落实在具体的代码分析上,我们按几个主线功能来走,蓝牙的开关、搜索配对、蓝牙耳机与电话和文件传输,

这几个也算是蓝牙的常用必备功能了,所以在后续文章中将按着这个顺序来跟一下它们代码调用流程。希望可以让你快速的了解蓝牙,当然如果有失误

写错的地方,欢迎反馈,谢谢。

4.2的蓝牙打开流程这一部分还是有些变化的,从界面上看蓝牙开关就是设置settings里那个switch开关,widget开关当然也可以,起点不同而已,后续的流程是一样的。先来看systemServer.java的代码,蓝牙服务开启的地方,最后一个else分支是我们关心的,前两个是模拟器的一个测试模式的。

[java] view plaincopy

  1. if (SystemProperties.get("ro.kernel.qemu").equals("1")) {
  2. Slog.i(TAG, "No Bluetooh Service (emulator)");
  3. } else if (factoryTest == SystemServer.FACTORY_TEST_LOW_LEVEL) {
  4. Slog.i(TAG, "No Bluetooth Service (factory test)");
  5. } else {
  6. Slog.i(TAG, "Bluetooth Manager Service");
  7. bluetooth = new BluetoothManagerService(context);
  8. ServiceManager.addService(BluetoothAdapter.BLUETOOTH_MANAGER_SERVICE, bluetooth);
  9. }

暂且看下bluetoothManagerService的构造方法,代码有点多,我们只看两个地方, loadStoredNameAndAddress()是读取蓝牙打开默认名称的地方,isBluetoothPersistedStateOn()是判断是否已打开蓝牙的,如果已打开,后续操作要执行开启蓝牙的动作,前面那几行注册广播其中就有这个作用。

[java] view plaincopy

  1. BluetoothManagerService(Context context) {
  2. ...一些变量声明初始化...
  3. IntentFilter filter = new IntentFilter(Intent.ACTION_BOOT_COMPLETED);
  4. filter.addAction(BluetoothAdapter.ACTION_LOCAL_NAME_CHANGED);
  5. filter.addAction(Intent.ACTION_USER_SWITCHED);
  6. registerForAirplaneMode(filter);
  7. mContext.registerReceiver(mReceiver, filter);
  8. loadStoredNameAndAddress();
  9. if (isBluetoothPersistedStateOn()) {
  10. mEnableExternal = true;
  11. }
  12. }

回到界面开关那个看得着的地方,界面上开关就是BluetoothEnabler.java这个类了,而setBluetoothEnabled()则是具体开关动作。看下代码

[java] view plaincopy

  1. public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
  2. // Show toast message if Bluetooth is not allowed in airplane mode
  3. if (isChecked &&
  4. !WirelessSettings.isRadioAllowed(mContext, Settings.Global.RADIO_BLUETOOTH)) {
  5. Toast.makeText(mContext, R.string.wifi_in_airplane_mode, Toast.LENGTH_SHORT).show();
  6. // Reset switch to off
  7. buttonView.setChecked(false);
  8. }
  9. if (mLocalAdapter != null) {
  10. mLocalAdapter.setBluetoothEnabled(isChecked);
  11. }
  12. mSwitch.setEnabled(false);
  13. }

这里在判断是飞行模式不知道为什么没有return,如果是飞行模式会有提示toast弹出,既然这样源码为什么还要执行下面打开流程呢,也许是个bug?不细究这个了,继续看setBluetoothEnabled()方法做什么了,很明显mLocalAdapter(LocalBluetoothAdapter )只是个过渡,里面的 mAdapter(BluetoothAdapter)才是真正的主角,代码如下:

[java] view plaincopy

  1. public void setBluetoothEnabled(boolean enabled) {
  2. boolean success = enabled   ? mAdapter.enable() : mAdapter.disable();
  3. if (success) {
  4. setBluetoothStateInt(enabled
  5. ? BluetoothAdapter.STATE_TURNING_ON
  6. : BluetoothAdapter.STATE_TURNING_OFF);
  7. } else {
  8. .........
  9. }
  10. }

在BluetoothAdapter.java里可以看到一个单例模式的应用,主要提供给其它程序调用蓝牙的一些方法用的,外部程序想调用蓝牙的方法就要先用这个

拿到BluetoothAdapter对象,代码也简单看下吧,里面是典型的binder应用。

[java] view plaincopy

  1. public static synchronized BluetoothAdapter getDefaultAdapter() {
  2. if (sAdapter == null) {
  3. IBinder b = ServiceManager.getService(BLUETOOTH_MANAGER_SERVICE);
  4. if (b != null) {
  5. IBluetoothManager managerService = IBluetoothManager.Stub.asInterface(b);
  6. sAdapter = new BluetoothAdapter(managerService);
  7. } else {
  8. Log.e(TAG, "Bluetooth binder is null");
  9. }
  10. }
  11. return sAdapter;

此时我们更关心mAdapter.enable()的后续操作,外部其它应用到getDefaultAdapter()也是调用enable(),注意,到了BluetoothAdapter我们已经在framework层了,顺着BluetoothAdapter.java的enable()调用先回到BluetoothManagerService.java的enable(),再进一步来到BluetoothManagerService.java中的handleEnable()

方法,后面要跳转到新类了,贴出来一起看下,这部分好像不同版本还有些出入,不过核心的启动service是一样的,不影响理解。

[java] view plaincopy

  1. private void handleEnable(boolean persist, boolean quietMode) {
  2. synchronized(mConnection) {
  3. if ((mBluetooth == null) && (!mBinding)) {
  4. //Start bind timeout and bind
  5. Message timeoutMsg=mHandler.obtainMessage(MESSAGE_TIMEOUT_BIND);
  6. mHandler.sendMessageDelayed(timeoutMsg,TIMEOUT_BIND_MS);
  7. mConnection.setGetNameAddressOnly(false);
  8. Intent i = new Intent(IBluetooth.class.getName());
  9. if (!mContext.bindService(i, mConnection,Context.BIND_AUTO_CREATE,
  10. UserHandle.USER_CURRENT)) {
  11. mHandler.removeMessages(MESSAGE_TIMEOUT_BIND);
  12. Log.e(TAG, "Fail to bind to: " + IBluetooth.class.getName());
  13. } else {
  14. mBinding = true;
  15. }
  16. }

下面跑到哪个service里去了呢,在log信息里可以看到"ActivityManager: Start proc com.android.bluetooth for service com.android.bluetooth/.btservice.AdapterService:"

这样的信息,那就是去AdapterService里看看,里面一共有三个enable(),跳转关系不复杂,我们直接看最后一个关键的。

[java] view plaincopy

  1. public synchronized boolean enable(boolean quietMode) {
  2. enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
  3. "Need BLUETOOTH ADMIN permission");
  4. if (DBG)debugLog("Enable called with quiet mode status =  " + mQuietmode);
  5. mQuietmode  = quietMode;
  6. Message m =
  7. mAdapterStateMachine.obtainMessage(AdapterState.USER_TURN_ON);
  8. mAdapterStateMachine.sendMessage(m);
  9. return true;
  10. }

状态机来了,状态转换图,从一个状态接受命令跳到另一个状态,因为我们是在开启蓝牙,所以先去的AdapterState.java内部类offstate.java里面找,在这个分支USER_TURN_ON看到mAdapterService.processStart();在这里面可以看到蓝牙遍历下所支持的profile,最后又发出个带AdapterState.STARTED标识的消息

处理在同文件下面的代码里

[java] view plaincopy

  1. case STARTED:   {
  2. if (DBG) Log.d(TAG,"CURRENT_STATE=PENDING, MESSAGE = STARTED, isTurningOn=" + isTurningOn + ", isTurningOff=" + isTurningOff);
  3. //Remove start timeout
  4. removeMessages(START_TIMEOUT);
  5. //Enable
  6. boolean ret = mAdapterService.enableNative();
  7. if (!ret) {
  8. Log.e(TAG, "Error while turning Bluetooth On");
  9. notifyAdapterStateChange(BluetoothAdapter.STATE_OFF);
  10. transitionTo(mOffState);
  11. } else {
  12. sendMessageDelayed(ENABLE_TIMEOUT, ENABLE_TIMEOUT_DELAY);
  13. }

看到那个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 plaincopy

  1. static jboolean enableNative(JNIEnv* env, jobject obj) {
  2. ALOGV("%s:",__FUNCTION__);
  3. jboolean result = JNI_FALSE;
  4. if (!sBluetoothInterface) return result;
  5. int ret = sBluetoothInterface->enable();
  6. result = (ret == BT_STATUS_SUCCESS) ? JNI_TRUE : JNI_FALSE;
  7. return result;
  8. }

代码瞬间简洁了不少,看来更多的故事还在下面,sBluetoothInterface这是什么,直接关系到下一步去哪的问题,看下变量声明,原来是

Const bt_interface_t *sBluetoothInterface = NULL; 再去找在哪初始化,搜索external目录可以找到/external/bluetooth/bluedroid/btif/src/bluetooth.c

[cpp] view plaincopy

  1. static const bt_interface_t bluetoothInterface = {
  2. sizeof(bt_interface_t),
  3. init,
  4. enable,
  5. disable,
  6. .............
  7. start_discovery,
  8. cancel_discovery,
  9. create_bond,
  10. remove_bond,
  11. cancel_bond,
  12. ...............
  13. };

原来在这里,说下怎么找到,直接跳转是不成了,看这个文件夹下的mk文件,那里面有libhardware目录是编译的时候要用到,这个多半在hardware目录里,在这里面很快可以看到bluetooth.h,那里面有最我们要找的结构体定义,头文件找到了,再找同名C文件就快了,好了继续吧看下enable()里是怎么实现的

[cpp] view plaincopy

  1. static int enable( void )
  2. {
  3. ALOGI("enable");
  4. /* sanity check */
  5. if (interface_ready() == FALSE)
  6. return BT_STATUS_NOT_READY;
  7. return btif_enable_bluetooth();
  8. }

又是一个新函数,直接跳转,比起刚才的寻觅这太幸福了

[cpp] view plaincopy

  1. bt_status_t btif_enable_bluetooth(void)
  2. {
  3. BTIF_TRACE_DEBUG0("BTIF ENABLE BLUETOOTH");
  4. if (btif_core_state != BTIF_CORE_STATE_DISABLED)
  5. {
  6. ALOGD("not disabled\n");
  7. return BT_STATUS_DONE;
  8. }
  9. btif_core_state = BTIF_CORE_STATE_ENABLING;
  10. /* Create the GKI tasks and run them */
  11. bte_main_enable(btif_local_bd_addr.address);
  12. return BT_STATUS_SUCCESS;
  13. }

忘了写路径了 好在可以直接跳转,下面是/external/bluetooth/bluedroid/main/bte_main.c,有点长,暂时只关心set_power那部分就好了,

[cpp] view plaincopy

  1. void bte_main_enable(uint8_t *local_addr)
  2. {
  3. APPL_TRACE_DEBUG1("%s", __FUNCTION__);
  4. ........................
  5. #if (defined (BT_CLEAN_TURN_ON_DISABLED) && BT_CLEAN_TURN_ON_DISABLED == TRUE)
  6. APPL_TRACE_DEBUG1("%s  Not Turninig Off the BT before Turninig ON", __FUNCTION__);
  7. #else
  8. /* toggle chip power to ensure we will reset chip in case
  9. a previous stack shutdown wasn‘t completed gracefully */
  10. bt_hc_if->set_power(BT_HC_CHIP_PWR_OFF);
  11. #endif
  12. bt_hc_if->set_power(BT_HC_CHIP_PWR_ON);
  13. bt_hc_if->preload(NULL);
  14. }
  15. .............................
  16. }

路径在这里/external/bluetooth/bluedroid/hci/src/bt_hci_bdroid.c,看看set_power里面有什么,快到头了

[cpp] view plaincopy

  1. static void set_power(bt_hc_chip_power_state_t state)
  2. {
  3. int pwr_state;
  4. BTHCDBG("set_power %d", state);
  5. /* Calling vendor-specific part */
  6. pwr_state = (state == BT_HC_CHIP_PWR_ON) ? BT_VND_PWR_ON : BT_VND_PWR_OFF;
  7. if (bt_vnd_if)
  8. bt_vnd_if->op(BT_VND_OP_POWER_CTRL, &pwr_state);
  9. else
  10. ALOGE("vendor lib is missing!");
  11. }

这下又有新东西了bt_vnd_if,这个是什么,bt_vendor_interface_t *bt_vnd_if=NULL;和刚才的bt_interface_t 一样,我们希望可以找到它的初始化,那样就可以继续跟踪了,不过看到下面的代码和注释,在源码中我们要绝望了。路径:/external/bluetooth/bluedroid/hci/include/bt_vendor_lib.h

[cpp] view plaincopy

  1. /* Entry point of DLib --
  2. *      Vendor library needs to implement the body of bt_vendor_interface_t
  3. *      structure and uses the below name as the variable name. HCI library
  4. *      will use this symbol name to get address of the object through the
  5. *      dlsym call.
  6. */
  7. extern const bt_vendor_interface_t BLUETOOTH_VENDOR_LIB_INTERFACE;
  8. bt_vendor_interface_t *bt_vnd_if=NULL;

google定义好了接口,具体实现要看vendor厂商来做了,这后面怎么实现就看各家芯片商怎么写了,肯定各有不同,而且这一部分代码一般是不会公开,当然授权购买后除外了。所以在4.2的源码中我们只跟到这里了,那后面会做什么呢,加载驱动和上电这两项肯定要有了,打开蓝牙没这两步怎么行,类似下面的字符串

[cpp] view plaincopy

  1. static const char* BT_DRIVER_MODULE_PATH =    "/system/lib/modules/mbt8xxx.ko";
  2. static const char* BT_DRIVER_MODULE_NAME =     "bt8xxx";
  3. static const char* BT_DRIVER_MODULE_INIT_ARG = " init_cfg=";
  4. static const char* BT_DRIVER_MODULE_INIT_CFG_PATH = "bt_init_cfg.conf";

在有类似下面的动作,insmod加载驱动,rfkill控制上下电,具体厂商具体做法也不同。

[cpp] view plaincopy

  1. ret = insmod(BT_DRIVER_MODULE_PATH, arg_buf);
  2. ret = system("/system/bin/rfkill block all");

写到这,关于4.2源码的蓝牙打开流程就算结束了,比起4.1之前的代码感觉没有以前的直观,对于vendor那部分的代码大家只能看各自厂商的代码了,一般蓝牙开启后才会上电,这样也比较符合逻辑和节省电量,是否上电可以连上手机用adb shell看sys/class/rfkill目录下的state状态值,有些厂商会把蓝牙和wifi的上电算在一起,这个也是要注意的,小心误判。最后呢,这次文章写得有点仓促,写错的或遗漏地方希望朋友指出来,非常感谢。

接上篇打开蓝牙继续,来一起看下蓝牙搜索的流程,触发蓝牙搜索的条件形式上有两种,一是在蓝牙设置界面开启蓝牙会直接开始搜索,另一个是先打开蓝牙开关在进入蓝牙设置界面也会触发搜索,也可能还有其它触发方式,但最后都要来到BluetoothSettngs.java的startScanning(),我们分析的起点也从这里开始,起步代码如下

[cpp] view plaincopy

  1. private void updateContent(int bluetoothState, boolean scanState) {
  2. if (numberOfPairedDevices == 0) {
  3. preferenceScreen.removePreference(mPairedDevicesCategory);
  4. if (scanState == true) {
  5. mActivityStarted = false;
  6. startScanning();
  7. } else<span style="font-family: Arial, Helvetica, sans-serif;">    ........</span>
  8. }
  9. private void startScanning() {
  10. if (!mAvailableDevicesCategoryIsPresent) {
  11. getPreferenceScreen().addPreference(mAvailableDevicesCategory);
  12. }
  13. mLocalAdapter.startScanning(true);
  14. }

其实在这里蓝牙搜索和打开流程是结构上是一致的,利用LocalBluetoothAdapter.java过渡到BluetoothAdapter.java再跳转至AdapterService.java要稍微留意下的是在这个过渡中startScaning()方法变成了startDiscovery()方法,看下代码:packages/apps/Settings/src/com/android/settings/bluetooth/LocalBluetoothAdapter.java

[java] view plaincopy

  1. void startScanning(boolean force) {
  2. if (!mAdapter.isDiscovering()) {
  3. if (!force) {
  4. // Don‘t scan more than frequently than SCAN_EXPIRATION_MS,
  5. // unless forced
  6. if (mLastScan + SCAN_EXPIRATION_MS > System.currentTimeMillis()) {
  7. return;
  8. }
  9. // If we are playing music, don‘t scan unless forced.
  10. A2dpProfile a2dp = mProfileManager.getA2dpProfile();
  11. if (a2dp != null && a2dp.isA2dpPlaying()) {
  12. return;
  13. }
  14. }
  15. //这里才是我们最关注的,前面限制条件关注一下就行了
  16. if (mAdapter.startDiscovery()) {
  17. mLastScan = System.currentTimeMillis();
  18. }
  19. }

BluetoothAdapter.java的那一段,路径 /frameworks/base/core/java/android/bluetooth/BluetoothAdapter.java

[java] view plaincopy

  1. public boolean startDiscovery() {
  2. .............................
  3. AdapterService service = getService();
  4. if (service == null) return false;
  5. return service.startDiscovery();
  6. }

这个service代码写得很明白AdapterService,转了一圈从framework又回到packages了,

下面的代码路径自然是 :packages/apps/Bluetooth/src/com/android/bluetooth/btservice/AdapterService.java,

[java] view plaincopy

  1. boolean startDiscovery() {
  2. enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
  3. "Need BLUETOOTH ADMIN permission");
  4. return startDiscoveryNative();
  5. }

和打开蓝牙根本就是一个套路,上面的流程略过一小步,很简单的不写了,下面要怎么走,估计大家也都猜到了,JNI应该出场了,

路径:/packages/apps/Bluetooth/jni/com_android_bluetooth_btservice_AdapterService.cpp

[cpp] view plaincopy

  1. static jboolean startDiscoveryNative(JNIEnv* env, jobject obj) {
  2. ALOGV("%s:",__FUNCTION__);
  3. jboolean result = JNI_FALSE;
  4. if (!sBluetoothInterface) return result;
  5. int ret = sBluetoothInterface->start_discovery();
  6. result = (ret == BT_STATUS_SUCCESS) ? JNI_TRUE : JNI_FALSE;
  7. return result;
  8. }

在下面要去哪?稍微要动下脑筋,不过我们在上一篇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 plaincopy

  1. static int start_discovery(void)
  2. {
  3. /* sanity check */
  4. if (interface_ready() == FALSE)
  5. return BT_STATUS_NOT_READY;
  6. return btif_dm_start_discovery();
  7. }

下面代码直接跳转就可以找到,路径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 plaincopy

  1. bt_status_t btif_dm_start_discovery(void)
  2. {
  3. tBTA_DM_INQ inq_params;
  4. tBTA_SERVICE_MASK services = 0;
  5. BTIF_TRACE_EVENT1("%s", __FUNCTION__);
  6. /* TODO: Do we need to handle multiple inquiries at the same time? */
  7. /* Set inquiry params and call API */
  8. #if (BLE_INCLUDED == TRUE)
  9. inq_params.mode = BTA_DM_GENERAL_INQUIRY|BTA_BLE_GENERAL_INQUIRY;
  10. #else
  11. inq_params.mode = BTA_DM_GENERAL_INQUIRY;
  12. #endif
  13. inq_params.duration = BTIF_DM_DEFAULT_INQ_MAX_DURATION;
  14. inq_params.max_resps = BTIF_DM_DEFAULT_INQ_MAX_RESULTS;
  15. inq_params.report_dup = TRUE;
  16. inq_params.filter_type = BTA_DM_INQ_CLR;
  17. /* TODO: Filter device by BDA needs to be implemented here */
  18. /* Will be enabled to TRUE once inquiry busy level has been received */
  19. btif_dm_inquiry_in_progress = FALSE;
  20. /* find nearby devices */
  21. BTA_DmSearch(&inq_params, services, bte_search_devices_evt);
  22. return BT_STATUS_SUCCESS;
  23. }

BTA_DmSearch()方法是看起来是要搜索了,不过里面这个家伙bte_search_devices_evt才是真正干活的主力,所以我们先看它,在这个函数里

[cpp] view plaincopy

  1. static void bte_search_devices_evt(tBTA_DM_SEARCH_EVT event, tBTA_DM_SEARCH *p_data)                                                                      {
  2. UINT16 param_len = 0;
  3. if (p_data)
  4. param_len += sizeof(tBTA_DM_SEARCH);
  5. /* Allocate buffer to hold the pointers (deep copy). The pointers will point to the end of the tBTA_DM_SEARCH */
  6. switch (event)
  7. {
  8. case BTA_DM_INQ_RES_EVT:
  9. {
  10. if (p_data->inq_res.p_eir)
  11. param_len += HCI_EXT_INQ_RESPONSE_LEN;
  12. }
  13. break;
  14. ..............................
  15. }
  16. BTIF_TRACE_DEBUG3("%s event=%s param_len=%d", __FUNCTION__, dump_dm_search_event(event), param_len);
  17. /* if remote name is available in EIR, set teh flag so that stack doesnt trigger RNR */
  18. if (event == BTA_DM_INQ_RES_EVT)
  19. p_data->inq_res.remt_name_not_required = check_eir_remote_name(p_data, NULL, NULL);
  20. btif_transfer_context (btif_dm_search_devices_evt , (UINT16) event, (void *)p_data, param_len,
  21. (param_len > sizeof(tBTA_DM_SEARCH)) ? search_devices_copy_cb : NULL);
  22. }

在上面的这个函数里又有这个bte_search_devices_evt,在它里我们能看一个 HAL_CBACK,这是要往回发消息了,看下这个函数的全貌,说是全貌,不过还是只贴出一个case分支,太长了,大家还是自行还源码吧。到这里已经可以知道扫描到蓝牙设备的mac地址和设备名,那个bdcpy函数就是在解析mac地址,有了这些,蓝牙搜索是到应该在界面展示成果的时候了,开始回调,忘记代码路径了,这个函数都在这个文件里:  /external/bluetooth/bluedroid/btif/src/btif_dm.c

[cpp] view plaincopy

  1. static void btif_dm_search_devices_evt (UINT16 event, char *p_param)
  2. tBTA_DM_SEARCH *p_search_data;
  3. BTIF_TRACE_EVENT2("%s event=%s", __FUNCTION__, dump_dm_search_event(event));
  4. switch (event)
  5. {
  6. case BTA_DM_DISC_RES_EVT:
  7. {
  8. p_search_data = (tBTA_DM_SEARCH *)p_param;
  9. /* Remote name update */
  10. if (strlen((const char *) p_search_data->disc_res.bd_name))
  11. {
  12. bt_property_t properties[1];
  13. bt_bdaddr_t bdaddr;
  14. bt_status_t status;
  15. properties[0].type = BT_PROPERTY_BDNAME;
  16. properties[0].val = p_search_data->disc_res.bd_name;
  17. properties[0].len = strlen((char *)p_search_data->disc_res.bd_name);
  18. bdcpy(bdaddr.address, p_search_data->disc_res.bd_addr);
  19. status = btif_storage_set_remote_device_property(&bdaddr, &properties[0]);
  20. ASSERTC(status == BT_STATUS_SUCCESS, "failed to save remote device property", status);
  21. HAL_CBACK(bt_hal_cbacks, remote_device_properties_cb,
  22. status, &bdaddr, 1, properties);
  23. }
  24. /* TODO: Services? */
  25. }
  26. break;

一小段log,下面的文字就在上面的函数里打出来的,即便上面的写的函数没有,肯定也在附近了。

05-30 13:52:14.890  1578  2612 D bt-btif : bte_search_devices_evt event=BTA_DM_INQ_RES_EVT param_len=524
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 plaincopy

  1. void BTA_DmSearch(tBTA_DM_INQ *p_dm_inq, tBTA_SERVICE_MASK services, tBTA_DM_SEARCH_CBACK *p_cback)
  2. {  tBTA_DM_API_SEARCH    *p_msg;
  3. if ((p_msg = (tBTA_DM_API_SEARCH *) GKI_getbuf(sizeof(tBTA_DM_API_SEARCH))) != NULL)
  4. {
  5. memset(p_msg, 0, sizeof(tBTA_DM_API_SEARCH));
  6. p_msg->hdr.event = BTA_DM_API_SEARCH_EVT;
  7. memcpy(&p_msg->inq_params, p_dm_inq, sizeof(tBTA_DM_INQ));
  8. p_msg->services = services;
  9. p_msg->p_cback = p_cback;
  10. p_msg->rs_res  = BTA_DM_RS_NONE;
  11. bta_sys_sendmsg(p_msg);
  12. }
  13. }

看了上面方法后我们 要回去了看看,代码通过JNI下来的,回去也是看JNI的回调方法

[cpp] view plaincopy

  1. method_deviceFoundCallback = env->GetMethodID(jniCallbackClass, "deviceFoundCallback", "([B)V");

deviceFoundCallback方法最后会来java层的/packages/apps/Bluetooth/src/com/android/bluetooth/btservice/RemoteDevices.java

[java] view plaincopy

  1. void deviceFoundCallback(byte[] address) {
  2. // The device properties are already registered - we can send the intent
  3. // now
  4. BluetoothDevice device = getDevice(address);
  5. debugLog("deviceFoundCallback: Remote Address is:" + device);
  6. DeviceProperties deviceProp = getDeviceProperties(device);
  7. if (deviceProp == null) {
  8. errorLog("Device Properties is null for Device:" + device);
  9. return;
  10. }
  11. Intent intent = new Intent(BluetoothDevice.ACTION_FOUND);
  12. intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device);
  13. intent.putExtra(BluetoothDevice.EXTRA_CLASS,
  14. new BluetoothClass(Integer.valueOf(deviceProp.mBluetoothClass)));
  15. intent.putExtra(BluetoothDevice.EXTRA_RSSI, deviceProp.mRssi);
  16. intent.putExtra(BluetoothDevice.EXTRA_NAME, deviceProp.mName);
  17. mAdapterService.sendBroadcast(intent, mAdapterService.BLUETOOTH_PERM);
  18. }

到这里就是给界面发广播,应用层收到广播显示出来,通过这个handle,这个handle可以在BluetoothEventManager.java的构造函数里找到,

[java] view plaincopy

  1. addHandler(BluetoothDevice.ACTION_FOUND, new DeviceFoundHandler());
  2. private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
  3. @Override
  4. public void onReceive(Context context, Intent intent) {
  5. String action = intent.getAction();
  6. BluetoothDevice device = intent
  7. .getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
  8. Handler handler = mHandlerMap.get(action);
  9. if (handler != null) {
  10. handler.onReceive(context, intent, device);
  11. }
  12. }
  13. };

这里handle对应要看DeviceFoundHandler,也就是下面贴出来的代码,

[java] view plaincopy

  1. private class DeviceFoundHandler implements Handler {
  2. public void onReceive(Context context, Intent intent,
  3. BluetoothDevice device) {
  4. ........................
  5. // TODO Pick up UUID. They should be available for 2.1 devices.
  6. // Skip for now, there‘s a bluez problem and we are not getting uuids even for 2.1.
  7. CachedBluetoothDevice cachedDevice = mDeviceManager.findDevice(device);
  8. if (cachedDevice == null) {
  9. cachedDevice = mDeviceManager.addDevice(mLocalAdapter, mProfileManager, device);
  10. Log.d(TAG, "DeviceFoundHandler created new CachedBluetoothDevice: "
  11. + cachedDevice);
  12. // callback to UI to create Preference for new device
  13. dispatchDeviceAdded(cachedDevice);
  14. }
  15. ......................
  16. }
  17. }

在if语句中dispatchDeviceAdded()向界面分发消息,最后处理消息的地方在这里,已经到settings应用里了

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

[java] view plaincopy

  1. public void onDeviceAdded(CachedBluetoothDevice cachedDevice) {
  2. if (mDevicePreferenceMap.get(cachedDevice) != null) {
  3. return;
  4. }
  5. // Prevent updates while the list shows one of the state messages
  6. if (mLocalAdapter.getBluetoothState() != BluetoothAdapter.STATE_ON) return;
  7. if (mFilter.matches(cachedDevice.getDevice())) {
  8. createDevicePreference(cachedDevice);
  9. }
  10. }

上面代码中最后一个分支就是界面显示要做的事了,从settings界面开始再到settings界面显示出搜索到蓝牙结束,后面的代码不再写了,本文关心的东东到此结束。

[java] view plaincopy

  1. void createDevicePreference(CachedBluetoothDevice cachedDevice) {
  2. BluetoothDevicePreference preference = new BluetoothDevicePreference(
  3. getActivity(), cachedDevice);
  4. initDevicePreference(preference);
  5. mDeviceListGroup.addPreference(preference);
  6. mDevicePreferenceMap.put(cachedDevice, preference);
  7. }

到目前为止,包括前面的打开流程分析,还仅是针对代码流程做的分析,对于蓝牙协议方面东西还没有涉及,比如蓝牙是如何发现其它蓝牙设备,这个流程究竟是怎么工作还不是很清楚,后续会尽量关注这些问题,估计看起来就没那么容易,欢迎有经验的朋友指点一二,当然对于本文不足,欢迎拍砖讨论。分享是快乐的,谢谢!

时间: 2024-10-12 22:40:14

【转】android 蓝牙的相关文章

android蓝牙(二)——接收数据

在蓝牙开发中,我们有这样的一个需求:我们的android客户端要始终保持和蓝牙的连接,当蓝牙有数据返回的时候,android客户端就要及时的收取数据,当蓝牙没有数据返回的时候我们就要保持android客户端和蓝牙之间的连接.这个时候我们就要采取socket来实现和蓝牙之间的连接.做项目使用过http轮询去获取数据,但是发现那样总是有一定的弊端.于是就才用了socket方式去获取数据. 实现步骤:1.启动一个service去监听是否有数据返回.一旦有数据返回就启动一个线程去处理数据 2.处理完数据

Android蓝牙自动配对Demo,亲测好使!!!

蓝牙自动配对,即搜索到其它蓝牙设备之后直接进行配对,不需要弹出配对确认框或者密钥输入框. 转载请注明出处http://blog.csdn.net/qq_25827845/article/details/52400782 经过最近一段时间得研究,针对网上给出的案例.总结了一个亲测好使的Demo. 说明如下: 1.本Demo用来连接蓝牙设备HC-05,如果你要连接其他蓝牙设备,注意修改相关名字以及修改设备初试pin值. 2.将Demo安装在Android手机上,点击按钮,可以实现与目标蓝牙设备的自动

Android bluetooth介绍(二): android 蓝牙代码架构及其uart 到rfcomm流程

关键词:蓝牙blueZ  UART  HCI_UART H4  HCI  L2CAP RFCOMM  版本:基于android4.2之前版本 bluez内核:linux/linux3.08系统:android/android4.1.3.4作者:xubin341719(欢迎转载,请注明作者,请尊重版权谢谢)欢迎指正错误,共同学习.共同进步!!一.Android Bluetooth Architecture蓝牙代码架构部分(google 官方蓝牙框架) Android的蓝牙系统,自下而上包括以下一些

Android 蓝牙

转自:http://www.eoeandroid.com/thread-18993-7-1.html 对于一般的软件开发人员来说,蓝牙是很少用到的,尤其是Android的蓝牙开发,国内的例子很少     Android对于蓝牙开发从2.0版本的sdk才开始支持,而且模拟器不支持,测试至少需要两部手机,所以制约了很多技术人员的开发,刚巧这段时间公司有蓝牙开发的需求,我看了很多国内.国外的资料,又研究了一下J2ME的蓝牙开发(为了找找思路),虽然我想要的功能还没实现(我曾经在很多论坛里问了很多遍,苦

[Android]蓝牙基本操作

蓝牙是一种支持设备短距离传输数据的无线技术.android在2.0以后提供了这方面的支持.从查找蓝牙设备到能够相互通信要经过几个基本步骤(本机做为服务器):1.设置权限在manifest中配置 Xml代码    <uses-permission android:name="android.permission.BLUETOOTH"/> <uses-permission android:name="android.permission.BLUETOOTH_AD

[Android]蓝牙相关接口及方法

首先,要操作蓝牙,先要在AndroidManifest.xml里加入权限 <uses-permissionandroid:name="android.permission.BLUETOOTH_ADMIN" /> <uses-permissionandroid:name="android.permission.BLUETOOTH" /> 然后,看下api,Android所有关于蓝牙开发的类都在android.bluetooth包下,共有8个类

Android 蓝牙4.0 BLE

Android ble (Bluetooth Low Energy) 蓝牙4.0,也就是说API level >= 18,且支持蓝牙4.0的手机才可以使用. BLE是蓝牙4.0的核心Profile,主打功能是快速搜索,快速连接,超低功耗保持连接和传输数据,弱点是数据传输速率低,由于BLE的低功耗特点,因此普遍用于穿戴设备. 官方demo:http://developer.android.com/guide/topics/connectivity/bluetooth-le.html 官方demo(

如何实现android蓝牙开发 自动配对连接,并不弹出提示框

如何实现android蓝牙开发 自动配对连接,并不弹出提示框 之前做一个android版的蓝牙,遇到最大的难题就是自动配对. 上网查资料说是用反射createBond()和setPin(),但测试时进行配对还是会出现提示,但配对是成功了 我就开始查找怎么关闭这个蓝牙配对提示框,后面还是伟大的android源码帮助了我. 在源码 BluetoothDevice 类中还有两个隐藏方法 cancelBondProcess()和cancelPairingUserInput() 这两个方法一个是取消配对进

[Android]蓝牙系统

1.1 蓝牙技术简介 蓝牙(Bleuetooth)原是十世纪统一了丹麦的一个国王的名字,现取其"统一"的含义,用来意在统一无线局域网通讯的标准的蓝牙技术.蓝牙技 术是爱立信,IBM,Intel等世界5家著名大公司在1998年联合推出的一项无线通讯规范.随后成立的蓝牙技术特殊兴趣组织(SIG)来负责该技术的 开发和技术协议的制定,如今全世界已有1800多家公司加盟该组织,最近微软公司也正式加盟并成为SIG组织的领导成员之一.它以低成本的近距离无线连接 为基础,为移动通信设备建立一个短程无

Android蓝牙传感应用

Android手机一般以客户端的角色主动连接SPP协议设备(接上蓝牙模块的数字传感器),连接流程是: 1.使用registerReceiver注册BroadcastReceiver来获取蓝牙状态.搜索设备等消息:2.使用BlueAdatper的搜索:3.在BroadcastReceiver的onReceive()里取得搜索所得的蓝牙设备信息(如名称,MAC,RSSI):4.通过设备的MAC地址来建立一个BluetoothDevice对象: 5.由BluetoothDevice衍生出Bluetoo