这几天越学越觉得自己无知….
看这一张充满求知欲的脸~~
目前各种三方应用乱七八糟,一打开全是各种权限请求
像我这种安全意识极强的人,怎么能随意允许呢?
直接拒绝,然后应用退出,然后卸载,然后手机就安静了…开个小玩笑
现在安全意识都越来越强,相关部门也对应用的权限要求做了一定的规范
所以,一个app最好只申请必须要用的权限
开发一个基于蓝牙的应用,需要声明哪些权限?
很简单,就这四个
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN"/>
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
多问一句,这些权限都干嘛的?
不知道,反正加上之后蓝牙就好使
但这些权限肯定是在整个蓝牙通信流程中所要求的权限
回顾一下蓝牙通信流程
先通过配对过程形成通信通道。其中一台设备(可检测到的设备)需将自身设置为可接收传入的连接请求。另一台设备会使用服务发现过程找到此可检测到的设备。在可检测到的设备接受配对请求后,这两台设备会完成绑定过程,并在此期间交换安全密钥。二者会缓存这些密钥,以供日后使用。完成配对和绑定过程后,两台设备会交换信息。当会话完成时,发起配对请求的设备会发布已将其链接到可检测设备的通道。但是,这两台设备仍保持绑定状态,因此在未来的会话期间,只要二者在彼此的范围内且均未移除绑定,便可自动重新连接。
那也就是说是在调用蓝牙API时所要求的权限
那就先来看蓝牙API功能
开启蓝牙
通过intent的方式实现
Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);startActivityForResult(enableBtIntent, REQUEST_ENABLE_BT);
开启蓝牙所需要的权限是BLUETOOTH
/** <p>Requires {@link android.Manifest.permission#BLUETOOTH}
*/
@SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION) public static final String
ACTION_REQUEST_ENABLE = "android.bluetooth.adapter.action.REQUEST_ENABLE";
但还有一个直接enable的API:BluetoothAdapter#enable
所需要的权限是BLUETOOTH_ADMIN
@RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN)public boolean enable() {...}
同样,BluetoothAdapter#disable也需要该权限
蓝牙可检测性
Intent discoverableIntent =new Intent(BluetoothAdapter.ACTION_REQUEST_DISCOVERABLE);
discoverableIntent.putExtra(BluetoothAdapter.EXTRA_DISCOVERABLE_DURATION, 300);
startActivity(discoverableIntent);
如果此时你的设备未开启蓝牙,那么在开启可检测性时会自动开启蓝牙
开启可检测性所需要的权限是BLUETOOTH
/** <p>Requires {@link android.Manifest.permission#BLUETOOTH}
*/
@SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION) public static final String
ACTION_REQUEST_DISCOVERABLE = "android.bluetooth.adapter.action.REQUEST_DISCOVERABLE";
设备可检测性有三种模式
-
SCAN_MODE_CONNECTABLE_DISCOVERABLE,设备处于可检测到模式。
可配对,可连接
-
SCAN_MODE_CONNECTABLE设备未处于可检测到模式,也就是说不能被扫描到,但仍能收到连接请求,比如曾经配对的设备发来连接请求。
-
SCAN_MODE_NONE设备未处于可检测到模式,既不能被扫描到也无法收到连接请求。
通过action设置的设备可检测性时长默认是BluetoothDiscoverableEnabler#DISCOVERABLE_TIMEOUT_TWO_MINUTE=120s,可检测模式是第一种模式:可检测,可连接
当然你也可以修改,像上述代码中,修改了可检测性时长为300s
扫描设备
传统蓝牙扫描BluetoothAdapter#startDiscovery,BLE蓝牙扫描BluetoothLeScanner#startScan
具体蓝牙扫描功能可参见前文跟我一起学蓝牙基础篇(八)--蓝牙扫描
传统蓝牙扫描需要的权限是BLUETOOTH
@RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN)public boolean startDiscovery(){...}
BLE蓝牙扫描需要的权限是BLUETOOTH_ADMIN
@RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN)public void startScan(final ScanCallback callback) {...}
为了获取扫描结果,需要的权限是ACCESS_COARSE_LOCATION或者ACCESS_FINE_LOCATION
/** An app must hold
* {@link android.Manifest.permission#ACCESS_COARSE_LOCATION ACCESS_COARSE_LOCATION} or
* {@link android.Manifest.permission#ACCESS_FINE_LOCATION ACCESS_FINE_LOCATION} permission
* in order to get results.
*/
蓝牙扫描会搜索一定区域内的一起用蓝牙功能的设备,并请求获取远程设备的某些信息,包括设备名、类别、以及蓝牙mac地址。
借助搜索到的设备信息,本机设备可以发起配对、连接操作
那么配对和连接有什么区别呢?
配对是指两台设备知道彼此的存在,具有可用于身份验证的共享链路密钥,并且能够与彼此建立加密连接。
连接是指设备当前共享一个 RFCOMM 通道,并且能够向彼此传输数据。
当前的 Android Bluetooth API 要求规定,只有先对设备进行配对,然后才能建立 RFCOMM 连接。
在使用 Bluetooth API 发起加密连接时,系统会自动执行配对。
查询已配对设备
调用方法为BluetoothAdapter#getBondedDevices()
所需要的权限是BLUETOOTH
@RequiresPermission(Manifest.permission.BLUETOOTH)public Set<BluetoothDevice> getBondedDevices() {...}
如果蓝牙目前是关闭状态,那么读到的列表为空
蓝牙会将已配对的设备保存到bt_conf.conf文件中,在蓝牙每次开启后,就会重新从文件中读取已配对的设备列表
但我如果想要连接设备直接扫描不就好了,为什么要读取这个列表?
因为扫描过程是一个极其消耗蓝牙适配器资源的过程,会严重影响现有连接的带宽
所以在想要连接新设备时,可以看该设备是否存在已配对列表中,若存在,就不需要执行耗资源的扫描操作
而且,如果对方设备目前不可见,那么本机无法扫描到,此时想要连接就要看看是否已配对了,如果已配对,那么就可以直接发起连接
连接建立
常用的连接建立方式就是建立 RFCOMM 通道
准备一个服务器,开放一个BluetoothServerSocket并listening连接。在此情况下,另一台设备发起连接。
这种连接建立方式不需要设备提前配对,在尝试建立连接的过程,系统会自动发起配对,连接请求会一直阻塞,直到配对结束。
如果用户接受配对,即配对成功,那么RFCOMM连接就会建立成功
如果用户拒绝配对或者配对超时,那么RECOMM连接就会建立失败
连接的建立过程需要区分服务端和客户端
针对服务器端
第一步通过调用listenUsingRfcommWithServiceRecord获取BluetoothServerSocket。
所需要的权限是BLUETOOTH权限
@RequiresPermission(Manifest.permission.BLUETOOTH)public BluetoothServerSocket listenUsingRfcommWithServiceRecord(String name, UUID uuid){...}
顺便说一下这个方法
方法的作用就是往本机蓝牙的SDP服务里注册一条蓝牙服务,以便其他蓝牙设备可以通过SDP服务发现协议来发现该设备支持的服务
方法参数name就是要注册的serviceName,可以随意写。UUID 就是一种标准化的 128 位格式,可以在网上随意生成,是蓝牙服务的一种标志。只要不和其他蓝牙服务重合就可以。
第二步通过调用BluetoothServerSocket#accept() 开始listening连接请求.
这个其实和TCP的socket类似,就一直阻塞监听连接请求,除非服务端接受到连接请求或者是发生异常退出
当然,最后你如果想要结束现有连接,那就调用BluetoothServerSocket#close()
针对客户端
第一步借助蓝牙扫描或者是读取已配对设备列表拿到远程设备的BluetoothDevice对象
第二步使用 BluetoothDevice获取BluetoothSocket对象
通过调用BluetoothDevice#createRfcommSocketToServiceRecord获取BluetoothSocket
该方法所需要的权限是BLUETOOTH
@RequiresPermission(Manifest.permission.BLUETOOTH)public BluetoothSocket createRfcommSocketToServiceRecord(UUID uuid) throws IOException {...}
方法参数中的UUID就是刚才创建服务端时所用到的UUID.客户端和服务端使用同一个UUID
也就是说客户端创建一个套接字,支持该UUID
第三步就是去BluetoothSocket#connect了。
当客户端调用此方法时,就会借助SDP查找刚才使用的BluetoothDevice设备是否支持该UUID的蓝牙服务
connect的过程其实就是把使用同一个UUID客户端的套接字和服务端的套接字连接起来,建立RFCOMM通道的过程
同样,connect也是一个阻塞的过程,在连接超时或者是连接失败时会有IO异常
数据传输
在连接建立之后,客户端和服务端均有了自己的BluetoothSocket对象
可以获取输入和输出流,进行读写操作。这个就是一般的socket的通信了。
除了以上这些常用API,还有一些
比如,修改蓝牙名字BluetoothAdapter#setName,需要Bluetooth_ADMIN
@RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN)public boolean setName(String name) {...}
其他的可以自行去BluetoothAdapter中查看
其实从蓝牙核心系统架构角度来说,总共提供了三种服务
-
设备控制服务:用于修改蓝牙设备的行为和工作方式,比如开关蓝牙。由设备管理器DM负责
-
传输控制服务:用于创建、修改、释放通信单元,由链路管理器LM负责
-
数据服务:比如数据的分包和重组,由L2CAP资源管理器负责
开放给应用的功能也仅仅是一部分
总结一下就是
BLUETOOTH权限,一般是蓝牙通信相关权限,例如请求连接、接受连接和传输数据等。
BLUETOOTH_ADMIN 权限。大多数应用只是需利用此权限发现、配对本地蓝牙设备
ACCESS_FINE_LOCATION权限,获取蓝牙扫描结果时需要。该权限是dangerous,需要动态申请您的应用需要此权限
ACCESS_COARSE_LOCATION权限,获取蓝牙扫描结果时需要 如果应用适配 Android 9(API 级别 28)或更低版本,则声明ACCESS_COARSE_LOCATION 权限即可,否则的话要使用ACCESS_FINE_LOCATION 权限。
为什么蓝牙会需要位置权限?
因为蓝牙扫描可用于收集用户的位置信息。此类信息可能来自用户自己的设备,以及在商店和交通设施等位置使用的蓝牙信标。
咔,收尾收尾
意犹未尽?那就关注一下呗
https://mp.weixin.qq.com/s/JGciI5hSQl4DMsXX1n_Yww