Android6.0源码之蓝牙研究汇总(一)--from初学者

fang_fang_story

因为原先刚开始看蓝牙时比较匆忙,而且整个流程都不太懂,感觉遗漏了好多东西,打算从头分析,分析跟蓝牙相关的所有问题,所以如果对蓝牙有任何问题的,可以留言,一起探讨。要想明白整个流程,就要明白蓝牙服务何时启动?如何启动?以及那些蓝牙协议服务如何启动?先抓个开机log看一看

1,BluetoothService----蓝牙第一个服务

开机启动的蓝牙服务为BluetoothService,这个可以从SystemServer中看到。在开启后首先启动的是SystemServer。

i>,在SystemServer中会去判断蓝牙是否被禁止。

boolean disableBluetooth = SystemProperties.getBoolean("config.disable_bluetooth", false);

可以在系统属性system.prop文件中定义或者修改改字段的值,来决定是否禁用蓝牙。

ii>,接下来就是开启蓝牙服务

              // Skip Bluetooth if we have an emulator kernel
            // TODO: Use a more reliable check to see if this product should
            // support Bluetooth - see bug 988521
            if (isEmulator) {             
              Slog.i(TAG, "No Bluetooh Service (emulator)");
            } else if (mFactoryTestMode == FactoryTest.FACTORY_TEST_LOW_LEVEL) {
                Slog.i(TAG, "No Bluetooth Service (factory test)");
            } else if (!context.getPackageManager().hasSystemFeature
                       (PackageManager.FEATURE_BLUETOOTH)) {
                Slog.i(TAG, "No Bluetooth Service (Bluetooth Hardware Not Present)");
            } else if (disableBluetooth) {
                Slog.i(TAG, "Bluetooth Service disabled by config");
            } else {
                Slog.i(TAG, "Bluetooth Service");
                mSystemServiceManager.startService(BluetoothService.class);
            }

只有经过几个条件的筛选才会开启服务。

第一个条件isEmualator :如果是模拟设备的话,不会开启蓝牙服务,也就是说不支持蓝牙,这也就解释了为什么那些开发工具(Eclipse,as上启动的模拟器不支持蓝牙)。至于是不是模拟器跟system.prop文件的一个字段有关:

boolean isEmulator = SystemProperties.get("ro.kernel.qemu").equals("1");

如果属性文件未定义该字段,则默认为false或者0;

第二条件FackoryTestMode--工厂测试模式

 /**
    * Gets the current factory test mode.
     *
     * @return One of: {@link #FACTORY_TEST_OFF}, {@link #FACTORY_TEST_LOW_LEVEL},
     * or {@link #FACTORY_TEST_HIGH_LEVEL}.
     */
    public static int getMode() {
        return SystemProperties.getInt("ro.factorytest", FACTORY_TEST_OFF);
    }

这是获取工厂模式的方法,有三个可选项,默认获取到的为off状态

第三个条件:检查一下Features_bluetooth是否可用:即检查下该设备是否支持与其他设备间的蓝牙通信

    /**
      * Check whether the given feature name is one of the available
    * features as returned by {@link #getSystemAvailableFeatures()}.
     *
     * @return Returns true if the devices supports the feature, else
     * false.
     */
    public abstract boolean hasSystemFeature(String name);

第四个条件disableBluetooth,设备支持蓝牙,但是被配置为禁止了。具体可以参考i>。

经历过这么多的判断条件,跋山涉水,蓝牙的第一个服务终于是可以开启了。这个服务就是用于对BluetoothManagerService的管理,代码量很少,更像是一个高层,具体的有什么蓝牙需要还是需要在BluetoothManagerService进行处理

2,修改蓝牙默认名称(短暂跑题)

修改位置:Android/device/qcom/common/bdroid_buildcfg.h

修改内容:

#define BTM_DEF_LOCAL_NAME   "btName..."

强调一下,这是蓝牙默认名称的修改,至于蓝牙的重命名跟这个是两回事

蓝牙重命名涉及到的是BluetootManagerService的一个方法

storeNameAndAddress(String name, String address) {

用来存储重命名后的蓝牙名字,在进行开关机操作后蓝牙名字是通过该service中的

loadStoredNameAndAddress()

来加载name和address的,也就是两个字段

 Settings.Secure.putString(mContentResolver, SECURE_SETTINGS_BLUETOOTH_NAME, name);

 Settings.Secure.putString(mContentResolver, SECURE_SETTINGS_BLUETOOTH_ADDRESS, address);

也就是说,在第一次开机后此时BluetoohtManagerService中还未对名字和地址进行读取和存储,bluetooth蓝牙名称获取到的是.h文件中所规定的默认的蓝牙名称,在以后的使用中-------如果不进行备份和重置的操作------蓝牙名称取自BluetoothManagerService中的两个字段所存储的值。但如果进行了备份和重置操作的话,数据会被清空,此时只能去获取默认的蓝牙名称。这个流程可根据重点字段进行跟踪查询代码。

3,BluetoothManagerService---蓝牙要启动的第二个服务

i>,在BluetoothService的构造方法中会创建BluetoothManagerService对象

mBluetoothManagerService = new BluetoothManagerService(context);

ii>,在BluetoothService的start方法中会公布BluetoothManagerService服务,以供其他服务或者app使用

/**
     * Publish the service so it is accessible to other services and apps.
     */



publishBinderService(BluetoothAdapter.BLUETOOTH_MANAGER_SERVICE, mBluetoothManagerService);

启动这个之后,感觉就一发不可收拾了,各种蓝牙协议所对应的服务也就应运而生,那么到底是怎么产生的呢?这就需要好好分析一下这个文件中的代码。 接下来最好是对蓝牙代码已经有所熟悉的基础上进行,贴一下蓝牙相关代码位置: 1:\android\android\frameworks\base\core\java\android\bluetooth:蓝牙协议相关 2:\android\android\frameworks\base\services\core\java\com\android\server:蓝牙服务相关 3:\android\android\packages\apps\Bluetooth\src\com\android\bluetooth\:蓝牙协议相关 蓝牙ui相关的代码位于settings模块目录下,可自行查找

先对BluetoothManagerService.java中的代码进行分析

(一),监听飞行模式---是否禁用蓝牙

1>,给receiver添加监听的action

 private void registerForAirplaneMode(IntentFilter filter) {
        final ContentResolver resolver = mContext.getContentResolver();
        final String airplaneModeRadios = Settings.Global.getString(resolver,
                Settings.Global.AIRPLANE_MODE_RADIOS);
        final String toggleableRadios = Settings.Global.getString(resolver,
                Settings.Global.AIRPLANE_MODE_TOGGLEABLE_RADIOS);
        boolean mIsAirplaneSensitive = airplaneModeRadios == null ? true :
                airplaneModeRadios.contains(Settings.Global.RADIO_BLUETOOTH);
        if (mIsAirplaneSensitive) {
            filter.addAction(Intent.ACTION_AIRPLANE_MODE_CHANGED);
        }
    }

对于是否要添加action有一定的限制条件。判断下飞行模式下radio列表是否包含蓝牙

 /**
         * A comma separated list of radios that need to be disabled when airplane mode
         * is on. This overrides WIFI_ON and BLUETOOTH_ON, if Wi-Fi and bluetooth are
         * included in the comma separated list.
         */
        public static final String AIRPLANE_MODE_RADIOS = "airplane_mode_radios";

AIRPLANE_MODE_RADIOS字段中存储了当在飞行模式下需要被禁止的功能,至于在飞行模式时什么需要被禁止,在配置文件中有配置

<!-- Comma-separated list of bluetooth, wifi, and cell. -->
    <string name="def_airplane_mode_radios" translatable="false">cell,bluetooth,wifi,nfc,wimax</string>

根据列表中是否包含bluetooth来判断是否需要监听飞行模式的状态改变。

2>,对飞行模式的状态改变的监控

所要监控的action为Intent.ACTION_AIRPLANE_MODE_CHANGED

else if (Intent.ACTION_AIRPLANE_MODE_CHANGED.equals(action)) {
                synchronized(mReceiver) {
                    
                   //判断蓝牙所保存的状态是否不为关闭(Bluetooth_off)状态
                   if (isBluetoothPersistedStateOn()) {
                        
                        //判断飞行模式是否开启
                        if (isAirplaneModeOn()) {
                            
                            //保存蓝牙状态为飞行模式下的蓝牙开启状态--蓝牙是开启但是被禁用的状态 
                           persistBluetoothSetting(BLUETOOTH_ON_AIRPLANE);
                        } else {
                            
                           //保存蓝牙状态为开启状态--蓝牙开启且可用
                           persistBluetoothSetting(BLUETOOTH_ON_BLUETOOTH);
                        }
                    }

                    int st = BluetoothAdapter.STATE_OFF;
                    if (mBluetooth != null) {
                        try {
                            st = mBluetooth.getState();
                        } catch (RemoteException e) {
                            Log.e(TAG,"Unable to call getState", e);
                        }
                    }
                    Log.d(TAG, "state" + st);

                    if (isAirplaneModeOn()) {
                        // Clear registered LE apps to force shut-off
                        synchronized (this) {
                           
                           //清除记录那些打开ble低功耗蓝牙的特殊应用的记录
                            mBleAppCount = 0;
                            mBleApps.clear();
                        }
                            //检测当前状态是否是ble低功耗蓝牙开启的状态
                            if (st == BluetoothAdapter.STATE_BLE_ON) {
                            //if state is BLE_ON make sure you trigger disableBLE part
                            try {
                                if (mBluetooth != null) {
                                  //关闭掉ble低功耗蓝牙
                                  mBluetooth.onBrEdrDown();
                                    mEnableExternal = false;
                                }
                            } catch(RemoteException e) {
                                Log.e(TAG,"Unable to call onBrEdrDown", e);
                            }
                        } else if (st == BluetoothAdapter.STATE_ON){//判断是否是蓝牙开启状态
                            // disable without persisting the setting
                            Log.d(TAG, "Calling disable");
                            sendDisableMsg();//关闭掉蓝牙
                        }
                    } else if (mEnableExternal) {
                        // enable without persisting the setting
                        Log.d(TAG, "Calling enable");
                        sendEnableMsg(mQuietEnableExternal);
                    }
                }
            }

接下来涉及到一个mBleApps.clear()。这个貌似是蓝牙6.0的新东西(与蓝牙4.4.2相比,手里目前只有这两套代码)。有了一个新的概念,貌似想要把传统蓝牙与低功耗蓝牙分离开来。这个mBleApps是一个map集合

    /** Internal death rec list */
    Map<IBinder, ClientDeathRecipient> mBleApps = new HashMap<IBinder, ClientDeathRecipient>();

与这个集合相关的是Bluetooth low energy(ble)即蓝牙低功耗,BluetoothAdapter中提供了两个方法,一个是enableBLE用于往该集合中传入数据,一个是disableBLE用于移除该集合中的数据。

enableBLE的解释如下,解释的很清楚

大致意思就是说:一些特殊的应用可以调用enableBLE方法只打开ble低功耗蓝牙功能,低功耗蓝牙打开后不会去改变settings模块中蓝牙的开关状态。

如果在调用该方法时蓝牙已经处于是开启的状态那么该方法仅仅会把应用注册而不会去影响或者修改蓝牙的状态。

如果用户关闭了蓝牙开关,那么系统会去检测目前是否有特殊的应用注册了ble,如果有的话会保持ble的开启状态,以保证该应用对ble的正常使用。

该方法属于异步调用,调用之后会立刻返回,客户端需要监听ACTION_BLE_STATE_CHANGED广播来监听蓝牙状态的改变。

如果该方法调用返回true,那么适配器的状态会立刻从STATE_OFF切换至STATE_BLE_ON.

如果该方法返回为false,那就说明开启动作被其他禁止,比如飞行模式会禁止开启ble,或者蓝牙已经是打开状态了,此时再开启也会返回false。

看完这个方法的介绍大致就明白mBleApps的作用了,就是一些直接开启蓝牙ble的特殊应用,不过enable属于被隐藏的方法,不使用反射的话做apk开发是用不到的。

如此就可以理解上边儿代码的涵义了

(二),Name和Address保存入本地缓存/从本地缓存中取出

 Settings.Secure.putString(mContentResolver, SECURE_SETTINGS_BLUETOOTH_NAME, name);
Settings.Secure.putString(mContentResolver, SECURE_SETTINGS_BLUETOOTH_ADDRESS, address);

name会存入SECURE_SETTINGS_BLUETOOTH_NAME字段

address会存入SECURE_SETTINGS_BLUETOOTH_ADDRESS字段

(三),开启Service(IBluetohth)

Intent i = new Intent(IBluetooth.class.getName());
                            if (!doBind(i, mConnection,
                                    Context.BIND_AUTO_CREATE | Context.BIND_IMPORTANT,
                                    UserHandle.CURRENT)) {
                                mHandler.removeMessages(MESSAGE_TIMEOUT_BIND);
                            } else {
                                mBinding = true;

dobind方法用于开启蓝牙服务

01-01 23:18:17.150   885  1193 D BluetoothManagerService: Message: 30

........

01-01 23:18:17.156   885  1193 D BluetoothManagerService: Message: 30

........

01-01 23:18:17.157  2548  2548 E BluetoothPan: Could not bind to Bluetooth Pan Service with Intent { act=android.bluetooth.IBluetoothPan }

01-01 23:18:17.158  2548  2548 D LocalBluetoothProfileManager: Adding local MAP profile

........

01-01 23:18:17.168  2548  2548 D BluetoothMap: Create BluetoothMap proxy object

........

01-01 23:18:17.182   885  1193 D BluetoothManagerService: Message: 30
.......

01-01 23:18:17.186  2548  2548 E BluetoothMap: Could not bind to Bluetooth MAP Service with Intent { act=android.bluetooth.IBluetoothMap }
........

01-01 23:18:17.188   885  1193 D BluetoothManagerService: Message: 30

01-01 23:18:17.190  2548  2548 E BluetoothPbap: Could not bind to Bluetooth Pbap Service with Intent { act=android.bluetooth.IBluetoothPbap }

01-01 23:18:17.190  2548  2548 D LocalBluetoothProfileManager: LocalBluetoothProfileManager construction complete

初学者难免有遗漏、用词不当亦或考虑不周,若有不当之处,静待指出

未完待续。。。。。。。。。

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏函数式编程语言及工具

Akka(10): 分布式运算:集群-Cluster

   Akka-Cluster可以在一部物理机或一组网络连接的服务器上搭建部署。用Akka开发同一版本的分布式程序可以在任何硬件环境中运行,这样我们就可以确定以...

61490
来自专栏刘望舒

Android PMS处理APK的复制

在上一篇文章Android包管理机制之PackageInstaller安装APK中,我们学习了PackageInstaller是如何安装APK的,最后会将APK...

22750
来自专栏Coding01

深入浅出 Laravel Echo (3)

看完 public channel 的流程,我们该来说说怎么跑通 private channel 了。

29830
来自专栏知识分享

android的PowerManager和PowerManager.WakeLock

  学习android一段时间了,为了进一步了解android的应用是如何设计开发的,决定详细研究几个开源的android应用。从一些开源应用中吸收点东西,一边...

10240
来自专栏Android 研究

APK安装流程详解12——PMS中的新安装流程上(拷贝)

从上面一片文章我们知道InstallAppProgress里面最后更新的代码是调用到PackageManager#installPackageWithVerif...

16710
来自专栏JackeyGao的博客

用Python不务正业 - 第一弹

从这篇开始会做一个用Python不务正业专题, 记录Python一些一无是用但是很好玩的脚本.本期是一个终端乱弹的脚本.

8820
来自专栏菩提树下的杨过

Flash/Flex学习笔记(33):如何用As3协同Flash CS IDE控制MovieClip实例

AS3历经若干年的成长,已经完全进化为一门面向对象的(动态)语言,但很多介绍AS3的书籍上往往只注意了AS3语言本身,而淡化了如何跟Flash IDE协同开发。...

22780
来自专栏机器人网

一文教你从PLC编程菜鸟变成高手

PLC编程软件由系统程序和用户程序两部分组成。系统程序包括监控程序、编译程序、诊断程序等,主要用于管理全机、将程序语言翻译成机器语言,诊断机器故障。PLC编程软...

63950
来自专栏bboysoul

使用devstack安装openstack

早就想用devstack去安装openstack了,但是自己的电脑内存才4g真的太小了,最近上了8g内存,果断开虚拟机装devstack。 devstack的...

33030
来自专栏逸鹏说道

我这么玩Web Api(一)

帮助页面或用户手册(Microsoft and Swashbuckle Help Page) 前言   你需要为客户编写Api调用手册?你需要测试你的Api接口...

31950

扫码关注云+社区

领取腾讯云代金券