前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Android6.0源码之蓝牙研究汇总(一)--from初学者

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

作者头像
fanfan
发布2018-01-24 18:00:59
2.3K0
发布2018-01-24 18:00:59
举报
文章被收录于专栏:编程思想之路编程思想之路

fang_fang_story

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

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

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

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

代码语言:javascript
复制
boolean disableBluetooth = SystemProperties.getBoolean("config.disable_bluetooth", false);

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

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

代码语言:javascript
复制
              // 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文件的一个字段有关:

代码语言:javascript
复制
boolean isEmulator = SystemProperties.get("ro.kernel.qemu").equals("1");

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

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

代码语言:javascript
复制
 /**
    * 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是否可用:即检查下该设备是否支持与其他设备间的蓝牙通信

代码语言:javascript
复制
    /**
      * 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

修改内容:

代码语言:javascript
复制
#define BTM_DEF_LOCAL_NAME   "btName..."

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

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

代码语言:javascript
复制
storeNameAndAddress(String name, String address) {

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

代码语言:javascript
复制
loadStoredNameAndAddress()

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

代码语言:javascript
复制
 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对象

代码语言:javascript
复制
mBluetoothManagerService = new BluetoothManagerService(context);

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

代码语言:javascript
复制
/**
     * 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

代码语言:javascript
复制
 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列表是否包含蓝牙

代码语言:javascript
复制
 /**
         * 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字段中存储了当在飞行模式下需要被禁止的功能,至于在飞行模式时什么需要被禁止,在配置文件中有配置

代码语言:javascript
复制
<!-- 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

代码语言:javascript
复制
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集合

代码语言:javascript
复制
    /** 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保存入本地缓存/从本地缓存中取出

代码语言:javascript
复制
 Settings.Secure.putString(mContentResolver, SECURE_SETTINGS_BLUETOOTH_NAME, name);
代码语言:javascript
复制
Settings.Secure.putString(mContentResolver, SECURE_SETTINGS_BLUETOOTH_ADDRESS, address);

name会存入SECURE_SETTINGS_BLUETOOTH_NAME字段

address会存入SECURE_SETTINGS_BLUETOOTH_ADDRESS字段

(三),开启Service(IBluetohth)

代码语言:javascript
复制
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方法用于开启蓝牙服务

代码语言:javascript
复制
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

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

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

代码语言:javascript
复制
本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2016-11-25 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • fang_fang_story
  • 1,BluetoothService----蓝牙第一个服务
  • 2,修改蓝牙默认名称(短暂跑题)
  • 3,BluetoothManagerService---蓝牙要启动的第二个服务
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档