原址
SystemServer启动开始讲起,在SystemServer启动的时,会启动一个BluetoothService与BluetoothA2DPService的实例:

Code:

//     Skip Bluetooth if we have an emulator kernel
223             // TODO: Use a more reliable check to see if this product should
224             // support Bluetooth - see bug 988521
225             if (SystemProperties.get("ro.kernel.qemu").equals("1")) {
226                 Slog.i(TAG, "No Bluetooh Service (emulator)");
227             } else if (factoryTest == SystemServer.FACTORY_TEST_LOW_LEVEL) {
228                 Slog.i(TAG, "No Bluetooth Service (factory test)");
229             } else {
230                 Slog.i(TAG, "Bluetooth Service");
231                 bluetooth = new BluetoothService(context);
232                 ServiceManager.addService(BluetoothAdapter.BLUETOOTH_SERVICE, bluetooth);
233                 bluetooth.initAfterRegistration();
234                 bluetoothA2dp = new BluetoothA2dpService(context, bluetooth);
235                 ServiceManager.addService(BluetoothA2dpService.BLUETOOTH_A2DP_SERVICE,
236                                           bluetoothA2dp);
237                 bluetooth.initAfterA2dpRegistration();
238 
239                 int airplaneModeOn = Settings.System.getInt(mContentResolver,
240                         Settings.System.AIRPLANE_MODE_ON, 0);
241                 int bluetoothOn = Settings.Secure.getInt(mContentResolver,
242                     Settings.Secure.BLUETOOTH_ON, 0);
243                 if (airplaneModeOn == 0 && bluetoothOn != 0) {
244                     bluetooth.enable();
245                 }
246             }

SystemServer.Java 里,在addService()时

bluetooth = new BluetoothService(context);

ServiceManager.addService(BluetoothAdapter.BLUETOOTH_SERVICE, bluetooth);

bluetooth.initAfterRegistration();

bluetoothA2dp = new BluetoothA2dpService(context, bluetooth);

bluetooth.initAfterA2dpRegistration();

addService后,执行了红色initAfterRegistration()方法,该方法里发送了一个消息

mBluetoothState.sendMessage(BluetoothAdapterStateMachine.TURN_HOT),进入BluetoothAdapterStateMachine之后,TURN_HOT的处理有两处,到底是哪一处的处理呢,我们到BluetoothAdapterStateMachine的构造函数里去看,在BluetoothAdapterStateMachine的构造函数里,设置了初始化状态为setInitialState(mPowerOff);因此addService后的TURN_HOT,

进入的是PowerOff里的TURN_HOT。

1.1          蓝牙的状态

蓝牙状态如下:

·        Power off

这就是蓝牙模块没有初始化的状态,这时候硬件模块是出于没有上电的状态。

·        Warm up

这个状态就是给设备上电,使设备能够从没电到待机状态。

·        Hot off

Hot off我个人理解就是在模块上电了,出于一种待命的状态,如果收到了turn_on_coninue的命令时候就会去将蓝牙模块切换到工作状态。如果接收到了turn_cold的命令时候,设备就会断电进入power off状态。

·        Switching

这也是一个中间状态,需要继续接收命令。

·        Bluetooth on

这时蓝牙模块出于正常工作的状态。

1.2          蓝牙的使能

在上层应用中,蓝牙界面类是BluetoothSettins.java,在actionBar上还有一个开关,另外MENU里也有四个菜单项。

实现蓝牙开关逻辑处理的类是BluetoothEnabler.java,当我们打开或关闭开关时,会执行onCheckedChanged()方法,

if (mLocalAdapter != null) {

mLocalAdapter.setBluetoothEnabled(isChecked);

}

这里,LocalBluetoothAdapter的setBluetoothEnabled方法,然后调用了BluetoothAdapter.java的 enable(), 进而调用到了IBluetooth的 enable(),这里的IBluetooth对应的有一个IBluetooth.aidl,

由此可以知道,是通过进程间通信,调用到了BluetoothService.java里的enable(),BluetoothService.java里的enable()里,我们很高兴看到如下代码:

mBluetoothState.sendMessage(BluetoothAdapterStateMachine.USER_TURN_ON, saveSetting);

这里就是发送了一个USER_TURN_ON消息,而处理该消息的地方是在processMessage()里,然后再根据状态,确认是在哪个状态对该消息进行了处理,processMessage()对该消息的处理如下:

Code:

case USER_TURN_ON:

// starts turning on BT module, broadcast this out

broadcastState(BluetoothAdapter.STATE_TURNING_ON);

transitionTo(mWarmUp);

if (prepareBluetooth()) {

// this is user request, save the setting

if ((Boolean) message.obj) {

persistSwitchSetting(true);

}

// We will continue turn the BT on all the way to the BluetoothOn state

deferMessage(obtainMessage(TURN_ON_CONTINUE));

} else {

Log.e(TAG, "failed to prepare bluetooth, abort turning on");

transitionTo(mPowerOff);

broadcastState(BluetoothAdapter.STATE_OFF);

}

break;

上一小节中讲到的蓝牙的状态,透过代码可以看到,是通过transitionTo()方法来切换蓝牙的状态的。

1.3          调用流程

方法调用流程如下:

BluetoothSetting.java ------>

BluetoothEnable.java( onCheckedChanged() ) ------>

LocalBlutoothAdapter.java ( setBluetoothEnable() ) ------>

BluetoothAdapter.java( enable())  ------>

IBluetooth.aidl( enable() )  ------>

BluetoothService.java( enable() )  ------>

BluetoothAdapterStateMachine.java( enableNative() )  ------>

android_server_BluetoothService.cpp

2.         蓝牙的扫描

2.1          蓝牙扫描

蓝牙扫描的流程,结构比较清晰,根据代码,分析打描的流程如下:

上层应用层代码调用startScanning()方法,这个方法会LocalBluetoothAdapter.java的startScanning()方法,进而调用到framework里的BluetoothAdapter.java里,关于这点,是和打开蓝牙的流程是一致的,需要说有的是,调用LocalBluetoothAdapter.java里面的方法,最终都是调用到了BluetoothAdapter.java里,后面有其它类似的方法也是如此。

在BluetoothAdapter.java里,则是startDiscovery()方法来扫描蓝牙设备的,使用的也是IBluetooth.aidl的文件来实现进程间通信,进而调用到BluetoothService.java里的startDiscovery(),到了这里,之后就是本地方法startDiscoveryNative()了,本地方法调用到了android_server_BluetoothService.cpp里。

2.2          调用流程

方法调用流程如下:

BluetoothSetting.java ------>

LocalBlutoothAdapter.java (startScanning () ) ------>

BluetoothAdapter.java(startDiscovery ())  ------>

IBluetooth.aidl(startDiscovery () )  ------>

BluetoothService.java(startDiscovery () )  ------>

BluetoothAdapterStateMachine.java(startDiscoveryNative () )  ------>

android_server_BluetoothService.cpp

2.3          扫描结果

当扫描到了设备时,在android_server_BluetoothEventLoop.cpp里有一个方法:

static DBusHandlerResult event_filter(DBusConnection *conn, DBusMessage *msg,

void *data)

这个方法里各种事件的处理,其中扫描到蓝牙设备后的处理是

Code:

if (dbus_message_is_signal(msg,

"org.bluez.Adapter",

"DeviceFound")) {

char *c_address;

DBusMessageIter iter;

jobjectArray str_array = NULL;

if (dbus_message_iter_init(msg, &iter)) {

dbus_message_iter_get_basic(&iter, &c_address);

if (dbus_message_iter_next(&iter))

str_array =

parse_remote_device_properties(env, &iter);

}

if (str_array != NULL) {

env->CallVoidMethod(nat->me,

method_onDeviceFound,

env->NewStringUTF(c_address),

str_array);

} else

LOG_AND_FREE_DBUS_ERROR_WITH_MSG(&err, msg);

goto success;

}

这里会有一个标识“DeviceFound”,调用到方法则是  method_onDeviceFound,接下来的处理是

method_onDeviceFound = env->GetMethodID(clazz, "onDeviceFound",

"(Ljava/lang/String;[Ljava/lang/String;)V");

这样,通过JNI, C++的方法调到了上层java代码中的BluetoothEventLoop.java的方法onDeviceFound()中,最终调用addDevice(),从而添加一个扫描到的蓝牙设备,并最终添加到BluetoothDeviceProperties.java中去了。

Code:

private void addDevice(String address, String[] properties) {

BluetoothDeviceProperties deviceProperties =

mBluetoothService.getDeviceProperties();

deviceProperties.addProperties(address, properties);

……

}

2.4          进程间通信

扫描和使能在BluetoothAdapter.java中,在调用enable()和startDiscovery()时,都调用到了IBluetooth.aidl中,在Android中,. aidl是用于进程间通信的。

AIDL进程间通信,在BluetoothAdapter.java里,enable()方法如下:

Code:

public boolean enable() {

try {

return mService.enable();

} catch (RemoteException e) {Log.e(TAG, "", e);}

return false;

}

这里,我们发现IBluetooth service = IBluetooth.Stub.asInterface(b),在frameworks\base\core\java\android\bluetooth目录下,有一个IBluetooth.aidl,这里是用到了进程间通信,在android里,进程间通信常用aidl来实现,最终IBluetooth调用到了哪个地方,我们看下IBluetooth的实现, BluetoothService最终实现了IBluetooth(BluetoothService extends IBluetooth.Stub),因此调用enable方法是调用这里的enable()方法。

3.         蓝牙的配对与连接

3.1          蓝牙的配对

3.1.1    master

扫描到可用的蓝牙设备后,在BluetoothDevicePreference.java里,点击列表中的某一蓝牙设备,会根据各个设备的bondState,会有不同的流程:

Code:

int bondState = mCachedDevice.getBondState();

if (mCachedDevice.isConnected()) {

askDisconnect();

} else if (bondState == BluetoothDevice.BOND_BONDED) {

mCachedDevice.connect(true);

} else if (bondState == BluetoothDevice.BOND_NONE) {

pair();

}

即如果已边接,则会断开边接disconnect(profile),如果状态是已配对,则会去连接connect(true),如果状态是NONE,则会先配对pair()。这里配对和连接需要注意的是,

都会进入startPairing(),进一步进入调用到BluetoothServcie.java里的createBond();

这里做所的工作就是配对。

3.1.2      slave

被要求配对的一方,在BluetoothPairingRequest.java这个广播里,会接收到来自底层的一个配对请求,接收到BluetoothDevice.ACTION_PAIRING_REQUEST的ACTION,并弹出提示框(BluetoothPairingDialog.java),提示用户配对。

3.1.3    取消配对

DeviceProfilesSettings.java中,取消配对直接调用unpairDevice,最终会调用到CachedBluetoothDevice.java的unpair()方法,取消配对时,会直接断开连接了,调用disconnect(),取消配对时,也会根据状态,分别做不同的处理

Code:

int state = getBondState();

if (state == BluetoothDevice.BOND_BONDING) {

mDevice.cancelBondProcess();

}

if (state != BluetoothDevice.BOND_NONE) {

final BluetoothDevice dev = mDevice;

if (dev != null) {

final boolean successful = dev.removeBond();

if (successful) {

if (Utils.D) {

Log.d(TAG, "Command sent successfully:REMOVE_BOND " + describe(null));

}

} else if (Utils.V) {

Log.v(TAG, "Framework rejected command immediately:REMOVE_BOND " +

describe(null));

}

}

}

3.2          蓝牙的连接

配对后即可进行A2DP, FTP等操作,不同的业务,都会走各自的连接,如BluetoothHeadset.java/BluetoothA2dp.java都有各自的connect()方法,待后续各自的profile里,单独分析各自的连接功能。

3.3          相关的界面类:

BluetoothSettings.java

DeviceListPreferenceFragment.java

DeviceProfilesSettings.java

BluetoothPairingRequest.java

BluetoothPairingDialog.java

4.         重命名蓝牙设备

4.1          调用流程

重命名蓝牙设备是一个从上到下单线的流程,在BluetoothNameDialogFragment.java这个dialog里,直接调用setName(),一直往下调到BluetoothAdapter.java里的setName()里。

5.         蓝牙可见时间

5.1          调用流程

可被检测到的主要逻辑处理在BluetoothDiscoverableEnabler.java里,这个流程较为简单,直接在BluetoothDiscoverableEnabler.java这个类里调用setEnable(),然后一步一步调用到BluetoothAdapter.java里的mService.setDiscoverableTimeout(timeout),进而调用到BluetoothService.java里的setDiscoverableTimeout()。

6.         接收到的文件

6.1          调用流程

当用户点击“接收到的文件”菜单时,会直接发送一个广播

Intent intent = new Intent(BTOPP_ACTION_OPEN_RECEIVED_FILES);

getActivity().sendBroadcast(intent);

这个广播将会被BluetoothOppReceiver.java接收到,进一步跳转到BluetoothOppTransferHistory.java中,列出所有接收到的文件。

Intent in = new Intent(context, BluetoothOppTransferHistory.class);

in.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP);

in.putExtra("direction", BluetoothShare.DIRECTION_INBOUND);

in.putExtra(Constants.EXTRA_SHOW_ALL_FILES, true);

context.startActivity(in);

接收到的文件功能实现是OPP里实现的,具体在OPP的分析中,会有进一步详细的分析。

7.          界面

蓝牙设置只有两个界面,其他的都是一些 dialog ,menu.。

7.1          蓝牙设置界面

进入蓝牙设置的界面:BluetoothSettings.java

已配对的蓝牙,进入后,可进行重命名,取消配对的界面:DeviceProfilesSettings

android 4.0 蓝牙分析之一相关推荐

  1. android 4.0 蓝牙分析之二

    原址 packages/apps/Settings/src/com/Android/settings/bluetooth/BluetoothSettings.Java onCreateOptionsM ...

  2. aptx android8,秒杀苹果无线音频!Android 8.0蓝牙音质支持aptxHD/LDAC

    原标题:秒杀苹果无线音频!Android 8.0蓝牙音质支持aptxHD/LDAC [TechWeb报道]8月25日消息,本周将迎来Android 8.0"奥利奥"系统推送,目前已 ...

  3. Android 9.0 蓝牙功能之一:蓝牙音乐

    Android 9.0 蓝牙功能之一:蓝牙音乐 本章节记录如何构建蓝牙音乐. 文章目录 Android 9.0 蓝牙功能之一:蓝牙音乐 主要流程 相关代码 其他要点: 蓝牙AG_EVENT广播 (手机 ...

  4. android 6.0蓝牙服务开启,Android应用开发之Android 6.0 蓝牙搜索不到设备原因,MIUI权限申请机制方法...

    本文将带你了解Android应用开发Android 6.0 蓝牙搜索不到设备原因,MIUI权限申请机制方法,希望本文对大家学Android有所帮助. 为提供更高的数据保护   Android6.0版本 ...

  5. android 6.0 蓝牙进程,Android6.0-蓝牙权限问题

    在Android 6.0,原来的蓝牙功能,发现扫描蓝牙设备时,无法获取到蓝牙设备:因为在6.0后,蓝牙这块增加一个动态权限:需要在程序中动态申请. 1)        在6.0版本前,使用蓝牙功能,只 ...

  6. Android 8.0 蓝牙唤醒 Ble 锁屏 保活 后台 持续扫描 进程拉活 自动唤醒

    主要是api的说明,嫌啰嗦的可以直接看demo,demo中有个检测锁屏时间重复开启扫描的代码,主要是如果APP没有获得电量或者后台运行的权限,只能持续后台运行几小时. 这个demo的作用是实现8.0以 ...

  7. android Ble4.0蓝牙开发之搜索慢、startLeScan()过时,6.0以上不需要定位权限也能快速搜索到蓝牙设备

    项目中需要用到android Ble蓝牙4.0开发技术,于是开启了蓝牙填坑之旅,说实话,蓝牙开发坑真多,跳出一个又进入下一个,每次遇到 问题,就觉得不可能解决了,还好在自己的摸索中,都一一的化解了,以 ...

  8. android 6.0蓝牙,Android 6.0 蓝牙搜索不到设备原因,MIUI权限申请机制方法

    为提供更高的数据保护 Android6.0版本上增加了关于Wifi和蓝牙的权限. 蓝牙搜索到设备需要用到定位服务,所以在开发中 targetSdkVersion 大于等于23(6.0) 需要在代码中进 ...

  9. Android 9.0 蓝牙通讯录 BluetoothPbapClient

    蓝牙通讯录主要包含联系人和通话记录 一.BluetoothPbapClient功能介绍 1.主要实现电话簿下载 2.电话号码簿访问协议(Phonebook Access Profile) 二.Blue ...

最新文章

  1. 注解方式使用 Redis 缓存
  2. 前端学习(3323):高级设计说闭包
  3. php 时间戳获取周几,PHP实现根据时间戳获取周几的方法,php戳获取周_PHP教程
  4. 云原生数据仓库TPC-H第一背后的Laser引擎大揭秘
  5. 挖漏经验:在密码重置请求包中添加X-Forwarded-Host实现受害者账户完全劫持
  6. JimuReport积木报表——如何设计一张带二维码的打印报表?
  7. 楼宇计算机网络是如何工作的,【干货】建筑楼宇智能化如何应用 4C 技术
  8. mysql sql语句 datediff_MySQL数据库之MySQL DATEDIFF 函数
  9. C#中的方法(函数),委托和事件
  10. 苏大计算机技术招生人数,苏大省内招生计划比去年增加86名
  11. 基于WEB的达内学子书城/商城源代码
  12. 坐标转换-换带计算(附软件下载)
  13. utf8字符集中漢字默認占三個字節
  14. narwal机器人_Narwal云鲸首创“基站+机器人”,智能扫拖干净省心
  15. Linux攻关之基础模块五 Vim
  16. 资本并非全部,新家装风口下如何迎风起舞?丨Xtecher 观察
  17. 牛客练习赛24 B凤 凰(并查集考察)
  18. 机器阅读理解算法集锦
  19. 无人驾驶之MATLAB无人驾驶工具箱学习(1)
  20. 根据url获取文件流MultipartFile

热门文章

  1. SQL SERVER 锁定的实例
  2. Linux 管道(pipe)原理及使用
  3. xcode 4,2 for Mac 10.6.8
  4. pad点餐系统 内存管理的一点总结
  5. 【ZooKeeper Notes 5】单机启动多个zk实例注意点
  6. GPT-J 自然语言处理 AI 模型
  7. .NET开发框架 ABP
  8. 基线理解_使用递归神经网络的上下文口语理解论文阅读
  9. webbrowser控件 加载为空白_OpenLayers教程五:地图控件之坐标拾取控件和鹰眼控件...
  10. linux上编译sdl,Linux下安装、编译SDL