我们在 Android 前台服务中使用 ScanFilter 来扫描信标。过滤器包括服务 UUID。当我们删除服务UUID时,我们可以检测具有指定uuid的信标。但是,当我们包含服务 UUID 时,信标检测失败。
private var scanFilter: ScanFilter = ScanFilter.Builder()
.setServiceUuid(ParcelUuid(UUID.fromString("0000feab-0000-1000-8000-00805f9b34fb"))) // Filter by Service UUID
.build()
如果我们删除了服务 UUID 过滤器,我们将得到以下我们想要在
onScanResult
回调中检测到的信标结果
ScanResult{device=CF:63:4B:5D:E1:F9, scanRecord=ScanRecord [mAdvertiseFlags=6, mServiceUuids=null, mServiceSolicitationUuids=[], mManufacturerSpecificData={}, mServiceData={0000feab-0000-1000-8000-00805f9b34fb=[64, 0, 2, 11, -30, 0, 1, -49, 99, 75, 93, -31, -7, 48, 10]}, mTxPowerLevel=0, mDeviceName=0001, mTransportBlocks=[]], rssi=-71, timestampNanos=40980732284950, eventType=27, primaryPhy=1, secondaryPhy=0, advertisingSid=255, txPower=127, periodicAdvertisingInterval=0}
在上面的结果中
mServiceUuids
为空,我认为这是问题所在,也许我正在检测的信标不是广播服务uuid,我可以基于mServiceData
应用过滤器,如下所示:
private val beaconUuid: ParcelUuid = ParcelUuid.fromString("0000feab-0000-1000-8000-00805f9b34fb")
private var serviceData: ByteArray = byteArrayOf(64, 0) // This is the beginning of the service data
private var serviceDataMask: ByteArray = byteArrayOf(-1, -1) // This mask means match both bytes exactly
private var scanFilter: ScanFilter = ScanFilter.Builder()
.setServiceData(beaconUuid, serviceData, serviceDataMask) // Filter by Service Data
.build()
我成功使用
mServiceData
和当前本机 BluethoothAdapter
过滤信标。但是,我需要切换到 AltBeacon 库以实现三星设备兼容性。当三星设备离线时,AltBeacon 基于 mServiceUuid
进行过滤,但我找不到使用此库按 mServiceData
进行过滤的方法。有人可以建议一个解决方案吗?
Android Beacon 库不将服务数据用于其扫描过滤器。 正如目前所写的,它设置了一个扫描过滤器,其中仅包含服务广告信标格式(如 Eddystone)的 ServiceUuid。 (对于 iBeacon 和 AltBeacon 等制造商广告,它会设置一个与制造商数据中的第一个字节匹配的扫描过滤器。)
执行此操作的库代码位于 ScanFitlerUtils 类此处。正如你所看到的,它只在过滤器中设置 UUID。
ScanFilter.Builder builder = new ScanFilter.Builder();
if (sfd.serviceUuid != null) {
// Use a 16 bit service UUID in a 128 bit form
String serviceUuidString = String.format("0000%04X-0000-1000-8000-00805f9b34fb", sfd.serviceUuid);
String serviceUuidMaskString = "FFFFFFFF-FFFF-FFFF-FFFF-FFFFFFFFFFFF";
ParcelUuid parcelUuid = ParcelUuid.fromString(serviceUuidString);
ParcelUuid parcelUuidMask = ParcelUuid.fromString(serviceUuidMaskString);
if (LogManager.isVerboseLoggingEnabled()) {
LogManager.d(TAG, "making scan filter for service: "+serviceUuidString+" "+parcelUuid);
LogManager.d(TAG, "making scan filter with service mask: "+serviceUuidMaskString+" "+parcelUuidMask);
}
builder.setServiceUuid(parcelUuid, parcelUuidMask);
}
图书馆没有设置与服务数据匹配的扫描过滤器的唯一原因是,迄今为止,使用 Eddystone 等服务广告的常见信标格式还没有必要。
如果您知道您的 ServiceUUID,这应该足以使用库进行匹配。 您显示的示例代码也与服务数据的前两个字节匹配。 (Android Beacon 库目前不支持此功能,但可以进行增强以实现此目的。但您也许可以接受仅与 UUID 匹配的过滤器,并让库以编程方式检查前两个字节。)
根据您的示例代码,我可以看到您正在使用 FEAB 的 16 位服务 UUID。 因此,使用该库,您可以像这样设置一个 BeaconParser:
val beaconManager = BeaconManager.getInstanceForApplication(this)
beaconManager.beaconParsers.add(BeaconParser().setBeaconLayout("s:0-1=feab,m:2-3=4000,p:3-3:-41,i:4-21v"))
上面的信标解析器表达式告诉库匹配您的信标,如下所示:
s:0-1=feab
使用 16 位服务 UUID FEAB 十六进制查找服务广告m:2-3=4000
仅当服务数据的前两个字节为 40 00 十六进制(十进制 64 0)时才匹配培根p:3-3
用于信标中距离估计的测量功率值的占位符(如果您关心距离估计,可以将其更改为数据包中的实际字节位置)i:4-21v
获取服务数据中的所有剩余字节并将它们视为单个信标标识符。 如果您想要多个标识符,您可以使用更复杂的表达式将这些字节拆分为多个标识符。当三星屏幕关闭时,库将设置扫描过滤器,以匹配 FEAB 的 16 位服务 UUID。 但随后它将对检测进行后处理,以丢弃任何在服务数据中不具有 40 00 十六进制或 64 0 十进制的前两个字节的检测。
如上所示设置 BeaconParser 后,您可以使用该库来检测这些信标,并且它应该根据需要使用扫描过滤器来在屏幕关闭和后台进行检测。