在写《Android蓝牙开发系列文章-蓝牙音箱连接》时,计划细化出一篇讲解蓝牙设备类型的文章,现在它来了~
阅读其他内容,可以点击《Android蓝牙开发系列文章-策划篇》,或者扫描文章下方的二维码关注我个人的公众号哈~
为什么要讲解蓝牙设备的分类?
设备的类型是表征设备能力的属性,设备类型的不同决定了UI中图标显示、扫描设备过程中的过滤以及在连接过程中通过什么类型的Profile进行连接。
细一点讲:
不同的蓝牙设备类型在UI中会有不同的UI显示,例如,鼠标设备显示为鼠标图标,音箱设备会显示音箱图标,耳机设备会显示耳机图标。
在扫描过程中,我们需要搜索到目标设备,这个设备类型往往是其中的一个匹配项,只有配对上了,我们才认为这个设备是我们的目标设备,才会对该设备进行下一步的流程:配对、连接等等。
在连接过程中,我们需要针对设备类型不同,通过不同的Profile发起连接,例如,对于音箱设备,我们需要通过A2DP Profile进行连接(如果忘记了,可以回头再看一下《Android蓝牙开发系列文章-蓝牙音箱连接》),对于鼠标设备,我们需要通过Input profile进行连接。
其他需求也可能会用到设备的类型。
目录
1.通过COD进行设备类型区分
2.Demo演示
1.通过COD进行设备类型区分
我们在扫描到设备后,可以通过mac地址构造一个BluetoothDevice对象,然后调用getBluetoothClass()方法获取其设备类型。
1227 /**
1228 * Get the Bluetooth class of the remote device.
1229 *
1230 * @return Bluetooth class object, or null on error
1231 */
1232 @RequiresPermission(Manifest.permission.BLUETOOTH)
1233 public BluetoothClass getBluetoothClass() {
1234 final IBluetooth service = sService;
1235 if (service == null) {
1236 Log.e(TAG, "BT not enabled. Cannot get Bluetooth Class");
1237 return null;
1238 }
1239 try {
1240 int classInt = service.getRemoteClass(this);
1241 if (classInt == BluetoothClass.ERROR) return null;
1242 return new BluetoothClass(classInt);
1243 } catch (RemoteException e) {
1244 Log.e(TAG, "", e);
1245 }
1246 return null;
1247 }
BluetoothClass类持有一个mClass变量,我们利用该变量来分辨设备类型。
从代码的注释,我们可以看到该变量就是蓝牙设备的COD,这个变量是三个部分组成的。
private final int mClass;
282 /**
283 * Return the Bluetooth Class of Device (CoD) value including the
284 * {@link BluetoothClass.Service}, {@link BluetoothClass.Device.Major} and
285 * minor device fields.
286 *
287 * <p>This value is an integer representation of Bluetooth CoD as in
288 * Bluetooth specification.
289 *
290 * @see <a href="Bluetooth CoD">https://www.bluetooth.com/specifications/assigned-numbers/baseband</a>
291 *
292 * @hide
293 */
294 public int getClassOfDevice() {
295 return mClass;
296 }
这个32位变量用固定的某些位表示service Class/Major Class/Minor Class。为了表述更清晰一点,我画了一个图,如下所示。
第2位到12位表示Major和Minor Class的组合,第8位到第12位表示Major Class,第14位到第23位表示Service Class。
这里提到了三个名词,下面分别进行解释:
Service Class:
service class类型有如下几种,一个service表示蓝牙的一个能力,例如,蓝牙设备支持A2DP Service,则表示这个蓝牙设备可以作为一个音箱设备使用。
109 /**
110 * Defines all service class constants.
111 * <p>Each {@link BluetoothClass} encodes zero or more service classes.
112 */
113 public static final class Service {
114 private static final int BITMASK = 0xFFE000;
115
116 public static final int LIMITED_DISCOVERABILITY = 0x002000;
117 public static final int POSITIONING = 0x010000;
118 public static final int NETWORKING = 0x020000;
119 public static final int RENDER = 0x040000;
120 public static final int CAPTURE = 0x080000;
121 public static final int OBJECT_TRANSFER = 0x100000;
122 public static final int AUDIO = 0x200000;
123 public static final int TELEPHONY = 0x400000;
124 public static final int INFORMATION = 0x800000;
125
我们可以利用hasService(int)来判断是否支持对于的service。
127 /**
128 * Return true if the specified service class is supported by this
129 * {@link BluetoothClass}.
130 * <p>Valid service classes are the public constants in
131 * {@link BluetoothClass.Service}. For example, {@link
132 * BluetoothClass.Service#AUDIO}.
133 *
134 * @param service valid service class
135 * @return true if the service class is supported
136 */
137 public boolean hasService(int service) {
138 return ((mClass & Service.BITMASK & service) != 0);
139 }
140
Major Class:
Major Class表示设备的大类型,这个区分粒度相对来说比较大,通过查看代码,我们可以看到蓝牙设备分为了Computer、Phone等几大类。
151 public static class Device {
152 private static final int BITMASK = 0x1FFC;
153
154 /**
155 * Defines all major device class constants.
156 * <p>See {@link BluetoothClass.Device} for minor classes.
157 */
158 public static class Major {
159 private static final int BITMASK = 0x1F00;
160
161 public static final int MISC = 0x0000;
162 public static final int COMPUTER = 0x0100;
163 public static final int PHONE = 0x0200;
164 public static final int NETWORKING = 0x0300;
165 public static final int AUDIO_VIDEO = 0x0400;
166 public static final int PERIPHERAL = 0x0500;
167 public static final int IMAGING = 0x0600;
168 public static final int WEARABLE = 0x0700;
169 public static final int TOY = 0x0800;
170 public static final int HEALTH = 0x0900;
171 public static final int UNCATEGORIZED = 0x1F00;
172 }
........
确定蓝牙设备属于哪个蓝牙类型大类可以调用如下接口:
256
257 /**
258 * Return the major device class component of this {@link BluetoothClass}.
259 * <p>Values returned from this function can be compared with the
260 * public constants in {@link BluetoothClass.Device.Major} to determine
261 * which major class is encoded in this Bluetooth class.
262 *
263 * @return major device class component
264 */
265 public int getMajorDeviceClass() {
266 return (mClass & Device.Major.BITMASK);
267 }
每个蓝牙设备大类可以进行进一步细化,例如,Computer大类可以进一步划分为如下几个小类:
174 // Devices in the COMPUTER major class
175 public static final int COMPUTER_UNCATEGORIZED = 0x0100;
176 public static final int COMPUTER_DESKTOP = 0x0104;
177 public static final int COMPUTER_SERVER = 0x0108;
178 public static final int COMPUTER_LAPTOP = 0x010C;
179 public static final int COMPUTER_HANDHELD_PC_PDA = 0x0110;
180 public static final int COMPUTER_PALM_SIZE_PC_PDA = 0x0114;
181 public static final int COMPUTER_WEARABLE = 0x0118;
综上,我们可以先用判断service class的方式来判断设备的能力,如果满足我们的要求,再进一步判断一下设备的Major Class,如果通过Major Class还不能定位到你的设备,则再进一步确定设备的小类。
2.Demo演示
我们利用《Android蓝牙开发系列文章-蓝牙音箱连接》中的demo为基础,来看一下我们的音箱是否支持a2dp profile以及它的Major Class是否为AUDIO_VIDEO。
在设备扫描到S7音箱后,判断一下是否满足a2dp Profile和Major Class,如果满足预期,则对这个设备发起配对、连接,如果不满足,则继续搜索设备。
public void onReceive(Context context, Intent intent) {final String action = intent.getAction();Log.d(TAG, "onReceive intent = " + action);if(action.equals(BluetoothDevice.ACTION_FOUND)) {BluetoothDevice btdevice = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);final String address = btdevice.getAddress();final String deviceName = btdevice.getName();Log.d(TAG, "onReceive found device, deivce address = " + address + ",deviceName = " + deviceName);if(isTargetDevice(btdevice)) {BluetoothClass btClass = btdevice.getBluetoothClass();if(btClass.hasService(BluetoothClass.Service.AUDIO)) {Log.d(TAG, "btdevice: " + btdevice + "support a2dp profile");} else {Log.d(TAG, "btdevice: " + btdevice + "not support a2dp profile");return;}int majorClass = btClass.getMajorDeviceClass();Log.d(TAG, "btClass: " + btClass + "majorClass = " + majorClass);stopScan();mHandler.sendEmptyMessageDelayed(MSG_PAIR, DELAYT_TIMES);}}
相关日志打印如下,我们可以看到该音箱的确支持A2dp Profile,且其Major Class为1024,转换成16进制数为0x400,等于AUDIO_VIDEO类型。
--------- beginning of system
03-15 10:53:03.305 1311-1311/com.atlas.btdemo D/MainActivity: onReceive intent = android.bluetooth.device.action.FOUND
03-15 10:53:03.309 1311-1311/com.atlas.btdemo D/MainActivity: onReceive found device, deivce address = FC:58:FA:B4:45:EA,deviceName = S7
03-15 10:53:03.318 1311-1311/com.atlas.btdemo D/MainActivity: deivce :S7is target device
03-15 10:53:03.320 1311-1311/com.atlas.btdemo D/MainActivity: btdevice: FC:58:FA:B4:45:EAsupport a2dp profile
03-15 10:53:03.320 1311-1311/com.atlas.btdemo D/MainActivity: btClass: 260404majorClass = 1024
03-15 10:53:03.331 1311-1311/com.atlas.btdemo D/MainActivity: stop scan device
03-15 10:53:03.832 1311-1311/com.atlas.btdemo D/MainActivity: dispatchMessage, msg.what = 1
03-15 10:53:03.832 1311-1311/com.atlas.btdemo D/MainActivity: start pair device = FC:58:FA:B4:45:EA
这里想再补充一点,最后不要只根据Major Class来进行设备类型的判断,因为有些设备制作的不标准,即虽然是一个蓝牙音箱,但是它的Major Class却不是0x400。
那应该怎么判断呢?其实我们只需要通过Service Class来判断就行了,因为一个蓝牙设备支持了A2dp Profile,则它就具有了作为音箱的能力,我们完全可以认为它就是一个音箱,不需要其他的条件判断了~
好了,本篇文章就到了这里,主要讲了BluetoothClass这个类型以及结合经验给大家一点判断设备类型的建议~
如果想持续关注本博客内容,请扫描关注个人微信公众号,或者微信搜索:万物互联技术。