ZT android -- 蓝牙 bluetooth (二) 打开蓝牙
android -- 蓝牙 bluetooth (二) 打开蓝牙
4.2的蓝牙打开流程这一部分还是有些变化的,从界面上看蓝牙开关就是设置settings里那个switch开关,widget开关当然也可以,起点不 同而已,后续的流程是一样的。先来看systemServer.java的代码,蓝牙服务开启的地方,最后一个else分支是我们关心的,前两个是模拟器 的一个测试模式的。
- 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()是判断是否已打开蓝牙的,如果已打开,后续操作要执行开启蓝牙的动作,前面那几行注册广 播其中就有这个作用。
- 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()则是具体开关动作。看下代码
- 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)才是真正的主角,代码如下:
- 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.java里可以看到一个单例模式的应用,主要提供给其它程序调用蓝牙的一些方法用的,外部程序想调用蓝牙的方法就要先用这个
拿到BluetoothAdapter对象,代码也简单看下吧,里面是典型的binder应用。
- 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;
此时我们更关心mAdapter.enable()的后续操作,外部其它应用到getDefaultAdapter()也是调用enable(),注意,
到了BluetoothAdapter我们已经在framework层了,顺着BluetoothAdapter.java的enable()调用先回到
BluetoothManagerService.java的enable(),再进一步来到BluetoothManagerService.java
中的handleEnable()
方法,后面要跳转到新类了,贴出来一起看下,这部分好像不同版本还有些出入,不过核心的启动service是一样的,不影响理解。
- 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;
- }
- }
下面跑到哪个service里去了呢,在log信息里可以看到"ActivityManager: Start proc
com.android.bluetooth for service
com.android.bluetooth/.btservice.AdapterService:"
这样的信息,那就是去AdapterService里看看,里面一共有三个enable(),跳转关系不复杂,我们直接看最后一个关键的。
- 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;
- }
状态机来了,状态转换图,从一个状态接受命令跳到另一个状态,因为我们是在开启蓝牙,所以先去的AdapterState.java内部类offstate.java里面找,在这个分支USER_TURN_ON看到mAdapterService.processStart();在这里面可以看到蓝牙遍历下所支持的profile,最后又发出个带AdapterState.STARTED标识的消息
处理在同文件下面的代码里
- 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里面
- 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
- static const bt_interface_t bluetoothInterface = {
- sizeof(bt_interface_t),
- init,
- enable,
- disable,
- .............
- start_discovery,
- cancel_discovery,
- create_bond,
- remove_bond,
- cancel_bond,
- ...............
- };
原
来在这里,说下怎么找到,直接跳转是不成了,看这个文件夹下的mk文件,那里面有libhardware目录是编译的时候要用到,这个多半在
hardware目录里,在这里面很快可以看到bluetooth.h,那里面有最我们要找的结构体定义,头文件找到了,再找同名C文件就快了,好了继续
吧看下enable()里是怎么实现的
- static int enable( void )
- {
- ALOGI("enable");
- /* sanity check */
- if (interface_ready() == FALSE)
- return BT_STATUS_NOT_READY;
- return btif_enable_bluetooth();
- }
又是一个新函数,直接跳转,比起刚才的寻觅这太幸福了
- 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;
- }
忘了写路径了 好在可以直接跳转,下面是/external/bluetooth/bluedroid/main/bte_main.c,有点长,暂时只关心set_power那部分就好了,
- 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里面有什么,快到头了
- 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!");
- }
这下又有新东西了bt_vnd_if,这个是什么,bt_vendor_interface_t
*bt_vnd_if=NULL;和刚才的bt_interface_t
一样,我们希望可以找到它的初始化,那样就可以继续跟踪了,不过看到下面的代码和注释,在源码中我们要绝望了。路径:/external
/bluetooth/bluedroid/hci/include/bt_vendor_lib.h
- /* 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的源码中我们只跟到这里了,那后面会做什么呢,加载驱动和上电这两项肯定要有了,打开蓝牙没这两步怎么行,类似下
面的字符串
- 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控制上下电,具体厂商具体做法也不同。
- ret = insmod(BT_DRIVER_MODULE_PATH, arg_buf);
- ret = system("/system/bin/rfkill block all");
写到这,关于4.2源码的蓝牙打开流程就算结束了,比起4.1之前的代码感觉没有以前的直观,对于vendor那部分的代码大家只能看各自厂商的代码
了,一般蓝牙开启后才会上电,这样也比较符合逻辑和节省电量,是否上电可以连上手机用adb
shell看sys/class/rfkill目录下的state状态值,有些厂商会把蓝牙和wifi的上电算在一起,这个也是要注意的,小心误判。最后
呢,这次文章写得有点仓促,写错的或遗漏地方希望朋友指出来,非常感谢。
更多
0
- 12楼 reality_jie 前天 22:31发表 [回复]
- 顶一下表示支持!
- 9楼 syngalon螳螳 2013-10-26 15:35发表 [回复]
- 今天发现了一篇,看看是不是很像哈哈
http://blog.csdn.net/yutao52shi/article/details/12690353
- 4楼 windjkl 2013-06-20 10:38发表 [回复]
- 设置-打开蓝牙:出现以下错误是什么原因:
D/BluetoothAdapter( 3463): 657778856: getState() : mService = null. Returning STATE_OFF
D/LocalBluetoothManager( 3463): setting foreground activity to non-null context
D/BluetoothAdapter( 3463): 657778856: getState() : mService = null. Returning STATE_OFF
- Re: balmy 2013-06-20 21:37发表 [回复]
- 回
复windjkl:这个没遇到过呢,不了解你当时的环境,没啥好想法。另外答复你添加USB外置蓝牙模块的问题,也是我咨询来的,仅供参考吧。usb设备
应该都会有注册流程吧,这一部分好像android部分的源码没有,在linux的源码是有的,也就是android源码里kernel。目录下的类似这
个路径:kernel/drivers/bluetooth/btusb.c,按你说的已经识别出来是不是可以认为注册成功了?后续代码没见过,可能没有
也可能是没发现的,目前接触到都是走串口的,目前只知道这些,希望不会误导你。
- Re: windjkl 2013-06-21 08:29发表 [回复]
- 回复baimy1985:谢谢楼主,注册已经成功,现在疑问是service的启动?jb4.2是否还需要在初始化时启动d-bus,hciattach这些服务呢?我看有些方案商的init.rc中并没有这些服务?是不是换成别的方式呢?
- Re: xuexingyang 2013-06-20 19:27发表 [回复]
- 回复baimy1985:4.2中hcidump没有了,只有靠confi文件来开关了。
- 2楼 kuna 2013-06-01 15:16发表 [回复]
- 请教一下:
handleEnable(boolean persist, boolean quietMode)是怎么跳转到启动service的呢?
就
是“下面跑到哪个service里去了呢,在log信息里可以看到"ActivityManager: Start proc
com.android.bluetooth for service
com.android.bluetooth/.btservice.AdapterService:" ”这段。
- Re: balmy 2013-06-02 20:23发表 [回复]
- 回复kuna:IBluetooth.class.getName()这个当时的取值是AdapterService,可以看下面的4.2.2源码,也许可以帮助你
/packages/apps/Bluetooth/src/com/android/bluetooth/btservice/AdapterService.java 480行的
private static class AdapterServiceBinder extends IBluetooth.Stub
ZT android -- 蓝牙 bluetooth (二) 打开蓝牙的更多相关文章
- 【转】Android:Bluetooth 的打开和关闭--不错
原文网址:http://www.ifeegoo.com/android-turn-on-and-turn-off-bluetooth.html 摘要:Android 中打开和关闭 Bluetooth ...
- Bluetooth LE(低功耗蓝牙) - 第三部分
回顾 在本系列的前两篇文章中,我们已经了解了一些关于Bluetooth LE的背景并建立一个简单的Activity / Service框架. 在这篇文章中,我们将探讨Bluetooth LE的细节 ...
- Android Developer -- Bluetooth篇 开发实例之四 API详解
http://www.open-open.com/lib/view/open1390879771695.html 这篇文章将会详细解析BluetoothAdapter的详细api, 包括隐藏方法, 每 ...
- android -- 蓝牙 bluetooth (二) 打开蓝牙
4.2的蓝牙打开流程这一部分还是有些变化的,从界面上看蓝牙开关就是设置settings里那个switch开关,widget开关当然也可以,起点不同而已,后续的流程是一样的.先来看systemServe ...
- ZT android -- 蓝牙 bluetooth (三)搜索蓝牙
android -- 蓝牙 bluetooth (三)搜索蓝牙 分类: Android的原生应用分析 2013-05-31 22:03 2192人阅读 评论(8) 收藏 举报 bluetooth蓝牙s ...
- ZT android -- 蓝牙 bluetooth (五)接电话与听音乐
android -- 蓝牙 bluetooth (五)接电话与听音乐 分类: Android的原生应用分析 2013-07-13 20:53 2165人阅读 评论(9) 收藏 举报 蓝牙android ...
- ZT android -- 蓝牙 bluetooth (四)OPP文件传输
android -- 蓝牙 bluetooth (四)OPP文件传输 分类: Android的原生应用分析 2013-06-22 21:51 2599人阅读 评论(19) 收藏 举报 4.2源码AND ...
- ZT android -- 蓝牙 bluetooth (一) 入门
android -- 蓝牙 bluetooth (一) 入门 分类: Android的原生应用分析 2013-05-19 21:44 4543人阅读 评论(37) 收藏 举报 bluetooth4.2 ...
- Android开发之蓝牙(Bluetooth)操作(二)--修改本机蓝牙设备的可见性,并扫描周围可用的蓝牙设备
版权声明:本文为博主原创文章,未经博主允许不得转载. 一. 修改本机蓝牙设备的可见性 二. 扫描周围可用的蓝牙设备 Eg: 一. 清单文件AdroidManifest.xml: <?xml v ...
随机推荐
- [PY3]——合并多个字典或映射(collections模块中的ChainMap 类)
问题 现在有多个字典或者映射,你想将它们从逻辑上合并为一个单一的映射后执行某些操作, 比如查找值或者检查某些键是否存在. 解决方案 使用 collections 模块中的 ChainMap 类 Cha ...
- PHP命名规则
参考: http://nowhisky.diandian.com/post/2012-08-12/40033898638 就一般约定而言,类.函数和变量的名字应该是能够让代码阅读者能够容易地知道这些代 ...
- [转]Repeat Page Header on each Page for reports SSRS
本文转自:https://stackoverflow.com/questions/3475144/i-want-to-repeat-page-header-on-each-page-for-repor ...
- 如鹏网学习笔记(五)MySql基础
MySQL基础 一.数据库概念 1,网友装备信息.论坛帖子信息.QQ好友关系信息.学籍管理系统中的学生信息等都要“持久化”的保存到一个地方, 如果通过IO写到文件中,那么会非常麻烦,而且不利于多人共享 ...
- 记一次tomcat自动退出问题
问题 环境: centos/tomcat8/jdk1.8 最近遇到部署在服务器的tomcat总是过一段时间就自动结束进程 ; 通过监控tomcat 日志文件(tail -f ./logs/catali ...
- HDU X问题 中国剩余定理--求满足条件的个数
X问题 Time Limit: 1000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)Total Submiss ...
- 常用SEO优化
- VUE配置项结构
VUE配置项结构 config:项目的配置文件 index.js: 基础的配置信息 dev.env.js:开发环境配置信息 prod.env.js:线上环境配置信息 build: 项目打包所需要的内容 ...
- 测试canvas绘制旋转文字的性能
canvas 绘制各种动画效果时,我们经常会使用画布旋转,使绘制上去的元素有旋转的效果. 最近在项目中碰到了很严重的性能问题,经常排查发现是因为绘制批量文字时使用了画布旋转,且每行文字的旋转角度是不一 ...
- 001Spring Boot中使用MongoDB
01.下载MongoDB 点击标题链接,下载windows可用的MongoDB. 02.解压 将下载的压缩包放入C盘根目录(根据自己需要调整目录)---->解压到当前文件夹---->重命名 ...