查找附近的所有蓝牙设备(耳机、电话等),无需强制设备处于可发现模式

Find all Bluetooth devices (headsets, phones etc) nearby, without forcing the devices in discoverable mode

我的目标:

从我的 Android 应用程序中检测所有附近的蓝牙设备(电话、耳机等)。

这是 developer.android.com 中的一个很好的示例,它发现附近的蓝牙设备以及已配对设备的列表。

我的情况:

我打开了两个蓝牙耳机,但在成功扫描蓝牙后未检测到它们!所以我深入研究问题,在某处发现蓝牙耳机需要切换到配对模式以防被android检测到。

要在配对模式下切换耳机,我必须在打开耳机时长按电源按钮。是的,最后,蓝牙耳机现在可以通过我的应用程序的扫描检测到。

我的问题:

我希望无需在配对模式下切换耳机即可自动检测到我的耳机。无法找到一种方法来检测附近所有已打开的蓝牙设备。

所以,这是我在阅读博客、线程、文档、SO 答案等之后发现的关于 Android 蓝牙发现的全部内容。认为这可能对 post 我在这里的发现有用。

所以在有人开始阅读之前,我想澄清一下,我找不到任何方法来检测附近已打开但无法发现的蓝牙设备。

检测附近的所有蓝牙设备

我的主要目标是检测附近的所有蓝牙设备。为此,我们在 Android 中有一个 BluetoothAdapter class,它有一个 startDiscovery 功能来扫描附近的蓝牙设备。因此,这不符合我的目的,因为蓝牙设备需要被发现才能被 Android 的蓝牙扫描检测到。我想要的是检测附近的所有蓝牙设备而不强制它们被发现。我开始寻找实现此目标的解决方案。

我开始研究 Bluetooth protocols 并找到了用于蓝牙检测、连接和配对的底层协议。对于 Android,我发现没有 API 到 discover/detect BT 设备已打开但未处于可发现模式。

带有 class 0x00 的蓝牙设备被忽略

在不强制蓝牙设备进入可发现模式的情况下深入研究这个检测问题,我们发现当 运行 扫描附近的 BT 时,我们发现标榜为 class 0×00 的蓝牙设备被自动忽略设备。这个问题在几个地方都有说明。这里我列举了一些。

  1. Whosebug question
  2. Google group
  3. Discuss DEV
  4. Found in HTC devices

我试图找到解决此问题的方法,是的,我找到了一个,尽管文档和评论以及此解决方法都说它不适用于所有类型 Android设备。无论如何,备用扫描与默认 BT 扫描相同,但它做了一些额外的工作。它会在 BT 扫描成功后读取 Android 日志,并检查是否有任何 BT 设备在整个操作过程中被跳过。这是描述 here . There's a google group discussing about the same work-around here.

的简单技巧

上述解决方法也没有解决我的问题。蓝牙耳机需要可被发现才能被 BT 扫描检测到。当 Android 正在扫描附近的 BT 设备时,它们不会被跳过。他们根本找不到。

蓝牙页面扫描

然而,我又开始寻找解决方案,又发现了一些有趣的东西!这是来自另一个Whosebug answer,它说,可以知道蓝牙设备是否在附近,即使他处于不可发现的模式,通过知道他在第一名。我再次引用他的回答以供将来参考,

The technique is to try a PAGE request, sending all the 6 bytes that are composing the seeked Bluetooth host MAC identifier. A PAGE request allows one to connect with a Bluetooth slave when knowing his BT ADDR. Devices that are in undiscoverable mode does not respond to inquiry scan (device discovery intent), but they do respond to page scan which is used by a device wanting to connect to an another previously known device.

我找到了希望并开始寻找,我们如何在 Android 中启动页面扫描,但这次也失败了。无法找到任何类型的 API 来启动页面扫描。从 BluetoothAdapter class 的 Android 文档中,我了解到,当 BT 扫描开始时,

This usually involves an inquiry scan of about 12 seconds, followed by a page scan of each new device to retrieve its Bluetooth name.

Android 文档中有页面扫描的指示,但令人惊讶的是,在任何地方都没有关于页面扫描的其他文档。我放了一个Whosebug question查询

我们可以 "fool" android 通过将 BTID 列表附加到 "already paired" 列表吗?

不,我没有找到任何方法。我了解了 Android 的蓝牙配对是如何工作的。这里有一个nice flow diagram along with detailed reading manual。我们试图使用 Android 的隐藏 APIs 并且可以使用反射使用一些隐藏的 APIs。然后我们开始寻找隐藏的 APIs,我们可以用它来达到我的目的。但是,不幸的是,我们未能找到任何隐藏的 API 以编程方式在 Android 的已配对列表中添加 BT 设备。

这里有一些有用的链接,用于检查蓝牙配对、隐藏 APIs 以及如何通过反射调用它们以供将来参考。

  1. How to initiate pairing
  2. How to use reflection to work with Android's hidden APIs
  3. Here's a google group discussion I followed, some people are facing the same problem we've here

所以当 BT 设备处于可发现模式并且不在 Android 的配对列表中时,我可以配对它。就我而言,我使用了蓝牙耳机。它已打开并处于可发现模式。耳机的 BT 地址(MAC 地址)已知。所以我使用反射从我的代码中调用函数 createBond 并且它运行良好。设备已成功添加到 Android 的 "already-paired" 列表中。在四处寻找解决方案以达到我的目的时,我发现了一些有趣的东西......

关于已配对设备的有趣观察

我发现,android 会在内存中保留以前配对的设备,即使我将它从已配对列表中删除也是如此。让我们在这里分享一个案例研究和调查结果,以便更好地理解。就我而言,耳机之前已配对。

  1. Unpaired/forget 来自 Android
  2. 的耳机
  3. 然后关闭耳机。
  4. 然后开始扫描附近的蓝牙设备
  5. 当然没有找到耳机
  6. 但是我们查看了Android日志发现,Android记得那个耳机绑定状态(配对状态)的变化。
  7. 它扫描之前绑定的耳机并报告远程设备已关闭

这是打印的日志以及我们之前配对然后取消配对的耳机的 MAC 地址。

PREV_BOND_STATE = 11 // BOND_BONDING    (BluetoothDevice.java)
BOND_STATE = 10      // BOND_NONE
REASON = 4           // UNBOND_REASON_REMOTE_DEVICE_DOWN

再一次,我运行同样的测试,但这次我们打开了蓝牙耳机(不可发现)。这次也没有找到耳机,但有趣的是,这次打印了不同的日志。

PREV_BOND_STATE = 11 // BOND_BONDING    (BluetoothDevice.java)
BOND_STATE = 10      // BOND_NONE
REASON = 2           // UNBOND_REASON_AUTH_CANCELLED

REASON 已更改,因此我可以理解 Android 尝试连接蓝牙耳机。耳机未处于可发现模式,但 Android 可以找到它并尝试连接它,因为它之前在配对列表中。

此日志是在蓝牙扫描 运行 添加 ACTION_BOND_STATE_CHANGED 操作 intent.addAction 时打印的。

可以Android作为信标吗?

是的,但在那种情况下 Android 也需要将自己宣传为 BLE。这是 Android 中的新功能,到目前为止并非所有设备都支持此功能。这是一个支持蓝牙 LE 广告功能的 list of devices。但是,由于蓝牙 LE 广告具有非常低的功耗和高性能,我认为这将是 Android 的下一件大事。

这里有一些对未来研究有用的链接。

蓝牙操作的有用函数和库