Android 监视通过AltBeacon库从我的应用程序中发布广告的信标

Android 监视通过AltBeacon库从我的应用程序中发布广告的信标,android,ibeacon,altbeacon,ibeacon-android,Android,Ibeacon,Altbeacon,Ibeacon Android,我正在开发一个解决方案,使用该库以iBeacon格式进行广告和扫描。我所担心的是,库扫描所有设备,这很好,但在通过扫描的设备进行解析后,它还跟踪不是来自我的应用程序的广告的广告设备。有没有办法通过使用图书馆来解决这个问题?如果不是,还有什么替代方案可以解决这个问题。 对我来说,跟踪那些只在我的应用程序中做广告的广告信标是非常重要的 这是在通过AltBeacon库以iBeacon格式发布广告时使用的代码: BluetoothManager bluetoothManager = (B

我正在开发一个解决方案,使用该库以iBeacon格式进行广告和扫描。我所担心的是,库扫描所有设备,这很好,但在通过扫描的设备进行解析后,它还跟踪不是来自我的应用程序的广告的广告设备。有没有办法通过使用图书馆来解决这个问题?如果不是,还有什么替代方案可以解决这个问题。 对我来说,跟踪那些只在我的应用程序中做广告的广告信标是非常重要的

这是在通过AltBeacon库以iBeacon格式发布广告时使用的代码:

BluetoothManager bluetoothManager =
        (BluetoothManager) applicationContext.getSystemService(Context.BLUETOOTH_SERVICE);
if (bluetoothManager != null) {
    BluetoothAdapter mBluetoothAdapter = bluetoothManager.getAdapter();
    BluetoothLeAdvertiser mBluetoothLeAdvertiser = mBluetoothAdapter.getBluetoothLeAdvertiser();
    if (mBluetoothLeAdvertiser != null) {
        beacon = new Beacon.Builder()
                .setId1(userId)
                .setId2("1")
                .setId3("1")
                .setManufacturer(0x004C)
                .setTxPower(-75)
                .setDataFields(Arrays.asList(new Long[]{0l}))
                .build();
        beaconParser = new BeaconParser()
                .setBeaconLayout("m:2-3=0215,i:4-19,i:20-21,i:22-23,p:24-24");
        beaconTransmitter = new BeaconTransmitter(InventaSdk.getContext(), beaconParser);
        beaconTransmitter.setBeacon(beacon);
    }
}
编辑:

解析信标代码:

/**
 * Construct a Beacon from a Bluetooth LE packet collected by Android's Bluetooth APIs,
 * including the raw Bluetooth device info
 *
 * @param scanData The actual packet bytes
 * @param rssi The measured signal strength of the packet
 * @param device The Bluetooth device that was detected
 * @return An instance of a <code>Beacon</code>
 */
public Beacon fromScanData(byte[] scanData, int rssi, BluetoothDevice device) {
    return fromScanData(scanData, rssi, device, new Beacon());
}

protected Beacon fromScanData(byte[] bytesToProcess, int rssi, BluetoothDevice device, Beacon beacon) {
    BleAdvertisement advert = new BleAdvertisement(bytesToProcess);
    boolean parseFailed = false;
    Pdu pduToParse = null;
    int startByte = 0;
    ArrayList<Identifier> identifiers = new ArrayList<Identifier>();
    ArrayList<Long> dataFields = new ArrayList<Long>();

    for (Pdu pdu: advert.getPdus()) {
        if (pdu.getType() == Pdu.GATT_SERVICE_UUID_PDU_TYPE ||
                pdu.getType() == Pdu.MANUFACTURER_DATA_PDU_TYPE) {
            pduToParse = pdu;
            LogHelper.d(TAG, "Processing pdu type: "+pdu.getType()+bytesToHex(bytesToProcess)+" with startIndex: "+pdu.getStartIndex()+" endIndex: "+pdu.getEndIndex());
            break;
        }
        else {
            LogHelper.d(TAG, "Ignoring pdu type %02X "+ pdu.getType());
        }
    }
    if (pduToParse == null) {
        LogHelper.d(TAG, "No PDUs to process in this packet.");
        parseFailed = true;
    }
    else {
        byte[] serviceUuidBytes = null;
        byte[] typeCodeBytes = longToByteArray(getMatchingBeaconTypeCode(), mMatchingBeaconTypeCodeEndOffset - mMatchingBeaconTypeCodeStartOffset + 1);
        if (getServiceUuid() != null) {
            serviceUuidBytes = longToByteArray(getServiceUuid(), mServiceUuidEndOffset - mServiceUuidStartOffset + 1, false);
        }
        startByte = pduToParse.getStartIndex();
        boolean patternFound = false;

        if (getServiceUuid() == null) {
            if (byteArraysMatch(bytesToProcess, startByte + mMatchingBeaconTypeCodeStartOffset, typeCodeBytes)) {
                patternFound = true;
            }
        } else {
            if (byteArraysMatch(bytesToProcess, startByte + mServiceUuidStartOffset, serviceUuidBytes) &&
                    byteArraysMatch(bytesToProcess, startByte + mMatchingBeaconTypeCodeStartOffset, typeCodeBytes)) {
                patternFound = true;
            }
        }



        if (patternFound == false) {
            // This is not a beacon
            if (getServiceUuid() == null) {
                LogHelper.d(TAG, "This is not a matching Beacon advertisement. (Was expecting   "+byteArrayToString(typeCodeBytes)
                                    + ".The bytes I see are: "+
                            bytesToHex(bytesToProcess));

            } else {
                LogHelper.d(TAG, "This is not a matching Beacon advertisement. Was expecting "+
                        byteArrayToString(serviceUuidBytes)+
                        " at offset "+startByte + mServiceUuidStartOffset+"and "+byteArrayToString(typeCodeBytes)+
                        " at offset "+ startByte + mMatchingBeaconTypeCodeStartOffset + "The bytes I see are: "
                        + bytesToHex(bytesToProcess));
            }
            parseFailed = true;
            beacon =  null;
        } else {
            LogHelper.d(TAG, "This is a recognized beacon advertisement -- "+
                        byteArrayToString(typeCodeBytes)+"seen");
            LogHelper.d(TAG, "Bytes are: "+ bytesToHex(bytesToProcess));
        }

        if (patternFound) {
            if (bytesToProcess.length <= startByte+mLayoutSize && mAllowPduOverflow) {
                // If the layout size is bigger than this PDU, and we allow overflow.  Make sure
                // the byte buffer is big enough by zero padding the end so we don't try to read
                // outside the byte array of the advertisement
                LogHelper.d(TAG, "Expanding buffer because it is too short to parse: "+bytesToProcess.length+", needed: "+(startByte+mLayoutSize));
                bytesToProcess = ensureMaxSize(bytesToProcess, startByte+mLayoutSize);
            }
            for (int i = 0; i < mIdentifierEndOffsets.size(); i++) {
                int endIndex = mIdentifierEndOffsets.get(i) + startByte;

                if (endIndex > pduToParse.getEndIndex() && mIdentifierVariableLengthFlags.get(i)) {
                    LogHelper.d(TAG, "Need to truncate identifier by "+(endIndex-pduToParse.getEndIndex()));
                    // If this is a variable length identifier, we truncate it to the size that
                    // is available in the packet
                    int start = mIdentifierStartOffsets.get(i) + startByte;
                    int end = pduToParse.getEndIndex()+1;
                    if (end <= start) {
                        LogHelper.d(TAG, "PDU is too short for identifer.  Packet is malformed");
                        return null;
                    }
                    Identifier identifier = Identifier.fromBytes(bytesToProcess, start, end, mIdentifierLittleEndianFlags.get(i));
                    identifiers.add(identifier);
                }
                else if (endIndex > pduToParse.getEndIndex() && !mAllowPduOverflow) {
                    parseFailed = true;
                    LogHelper.d(TAG, "Cannot parse identifier "+i+" because PDU is too short.  endIndex: " + endIndex + " PDU endIndex: " + pduToParse.getEndIndex());
                }
                else {
                    Identifier identifier = Identifier.fromBytes(bytesToProcess, mIdentifierStartOffsets.get(i) + startByte, endIndex+1, mIdentifierLittleEndianFlags.get(i));
                    identifiers.add(identifier);
                }
            }
            for (int i = 0; i < mDataEndOffsets.size(); i++) {
                int endIndex = mDataEndOffsets.get(i) + startByte;
                if (endIndex > pduToParse.getEndIndex() && !mAllowPduOverflow) {
                    LogHelper.d(TAG, "Cannot parse data field "+i+" because PDU is too short.  endIndex: " + endIndex + " PDU endIndex: " + pduToParse.getEndIndex()+".  Setting value to 0");
                    dataFields.add(new Long(0l));
                }
                else {
                    String dataString = byteArrayToFormattedString(bytesToProcess, mDataStartOffsets.get(i) + startByte, endIndex, mDataLittleEndianFlags.get(i));
                    dataFields.add(Long.decode(dataString));
                }
            }

            if (mPowerStartOffset != null) {
                int endIndex = mPowerEndOffset + startByte;
                int txPower = 0;
                try {
                    if (endIndex > pduToParse.getEndIndex() && !mAllowPduOverflow) {
                        parseFailed = true;
                        LogHelper.d(TAG, "Cannot parse power field because PDU is too short.  endIndex: " + endIndex + " PDU endIndex: " + pduToParse.getEndIndex());
                    }
                    else {
                        String powerString = byteArrayToFormattedString(bytesToProcess, mPowerStartOffset + startByte, mPowerEndOffset + startByte, false);
                        txPower = Integer.parseInt(powerString)+mDBmCorrection;
                        // make sure it is a signed integer
                        if (txPower > 127) {
                            txPower -= 256;
                        }
                        beacon.mTxPower = txPower;
                    }
                }
                catch (NumberFormatException e1) {
                    // keep default value
                }
                catch (NullPointerException e2) {
                    // keep default value
                }
            }
        }
    }

    if (parseFailed) {
        beacon = null;
    }
    else {
        int beaconTypeCode = 0;
        String beaconTypeString = byteArrayToFormattedString(bytesToProcess, mMatchingBeaconTypeCodeStartOffset+startByte, mMatchingBeaconTypeCodeEndOffset+startByte, false);
        beaconTypeCode = Integer.parseInt(beaconTypeString);
        // TODO: error handling needed on the parse

        int manufacturer = 0;
        String manufacturerString = byteArrayToFormattedString(bytesToProcess, startByte, startByte+1, true);
        manufacturer = Integer.parseInt(manufacturerString);

        String macAddress = null;
        String name = null;
        if (device != null) {
            macAddress = device.getAddress();
            name = device.getName();
        }

        beacon.mIdentifiers = identifiers;
        beacon.mDataFields = dataFields;
        beacon.mRssi = rssi;
        beacon.mBeaconTypeCode = beaconTypeCode;
        if (mServiceUuid != null) {
            beacon.mServiceUuid = (int) mServiceUuid.longValue();
        }
        else {
            beacon.mServiceUuid = -1;
        }

        beacon.mBluetoothAddress = macAddress;
        beacon.mBluetoothName= name;
        beacon.mManufacturer = manufacturer;
        beacon.mParserIdentifier = mIdentifier;
        beacon.mMultiFrameBeacon = extraParsers.size() > 0 || mExtraFrame;
    }
    return beacon;
}
扫描回调:

private ScanCallback getNewLeScanCallback() {
    if (leScanCallback == null) {
        leScanCallback = new ScanCallback() {
            @MainThread
            @Override
            public void onScanResult(int callbackType, ScanResult scanResult) {
                    LogHelper.d(TAG, "got record");
                    List<ParcelUuid> uuids = scanResult.getScanRecord().getServiceUuids();
                    if (uuids != null) {
                        for (ParcelUuid uuid : uuids) {
                            LogHelper.d(TAG, "with service uuid: "+uuid);
                        }
                    }

                    try {
                        LogHelper.d("ScanRecord", "Raw Data: " + scanResult.toString());
                        LogHelper.d("ScanRecord", "Device Data Name: " + scanResult.getDevice().getName() + "Rssi: " + scanResult.getRssi() + "Address: " + scanResult.getDevice().getAddress() + "Service uuid: " + scanResult.getScanRecord().getServiceUuids());
                    }catch (Exception e){
                        LogHelper.d("ScanRecord",e.getMessage());
                        e.printStackTrace();
                    }
                mCycledLeScanCallback.onLeScan(scanResult.getDevice(),
                        scanResult.getRssi(), scanResult.getScanRecord().getBytes());
                if (mBackgroundLScanStartTime > 0) {
                    LogHelper.d(TAG, "got a filtered scan result in the background.");
                }
            }

            @MainThread
            @Override
            public void onBatchScanResults(List<ScanResult> results) {
                LogHelper.d(TAG, "got batch records");
                for (ScanResult scanResult : results) {
                    mCycledLeScanCallback.onLeScan(scanResult.getDevice(),
                            scanResult.getRssi(), scanResult.getScanRecord().getBytes());
                }
                if (mBackgroundLScanStartTime > 0) {
                    LogHelper.d(TAG, "got a filtered batch scan result in the background.");
                }
            }

            @MainThread
            @Override
            public void onScanFailed(int errorCode) {
                Intent intent = new Intent("onScanFailed");
                intent.putExtra("errorCode", errorCode);
                LocalBroadcastManager.getInstance(CycledLeScannerForLollipop.this.mContext).sendBroadcast(intent);
                switch (errorCode) {
                    case SCAN_FAILED_ALREADY_STARTED:
                        LogHelper.e(TAG, "Scan failed: a BLE scan with the same settings is already started by the app");
                        break;
                    case SCAN_FAILED_APPLICATION_REGISTRATION_FAILED:
                        LogHelper.e(TAG, "Scan failed: app cannot be registered");
                        break;
                    case SCAN_FAILED_FEATURE_UNSUPPORTED:
                        LogHelper.e(TAG, "Scan failed: power optimized scan feature is not supported");
                        break;
                    case SCAN_FAILED_INTERNAL_ERROR:
                        LogHelper.e(TAG, "Scan failed: internal error");
                        break;
                    default:
                        LogHelper.e(TAG, "Scan failed with unknown error (errorCode=" + errorCode + ")");
                        break;
                }
            }
        };
    }
    return leScanCallback;
}
private ScanCallback getNewLeScanCallback(){
if(leScanCallback==null){
leScanCallback=new ScanCallback(){
@主线
@凌驾
public void onScanResult(int callbackType,ScanResult ScanResult){
LogHelper.d(标记“got记录”);
List uuids=scanResult.getScanRecord().getServiceUuids();
如果(uuids!=null){
用于(PARCELUID uuid:uuid){
LogHelper.d(标签,“带有服务uuid:+uuid”);
}
}
试一试{
LogHelper.d(“ScanRecord”,“原始数据:+scanResult.toString());
LogHelper.d(“ScanRecord”,“设备数据名称:”+scanResult.getDevice().getName()+“Rssi:”+scanResult.getRssi()+“地址:”+scanResult.getDevice().getAddress()+“服务uuid:”+scanResult.getScanRecord().getServiceUuids());
}捕获(例外e){
LogHelper.d(“ScanRecord”,e.getMessage());
e、 printStackTrace();
}
mCycledLeScanCallback.onLeScan(scanResult.getDevice(),
scanResult.getRssi(),scanResult.getScanRecord().getBytes());
如果(mBackgroundLScanStartTime>0){
LogHelper.d(标记“在后台获得过滤的扫描结果”);
}
}
@主线
@凌驾
public void onBatchScanResults(列出结果){
LogHelper.d(标记“获取批次记录”);
用于(扫描结果扫描结果:结果){
mCycledLeScanCallback.onLeScan(scanResult.getDevice(),
scanResult.getRssi(),scanResult.getScanRecord().getBytes());
}
如果(mBackgroundLScanStartTime>0){
LogHelper.d(标签,“在后台获得过滤的批扫描结果”);
}
}
@主线
@凌驾
公共无效OnScan失败(内部错误代码){
意向意向=新意向(“onScanFailed”);
intent.putExtra(“错误代码”,错误代码);
LocalBroadcastManager.getInstance(cycledlescannerForLipop.this.mContext).sendBroadcast(intent);
开关(错误代码){
案例扫描\u失败\u已\u启动:
LogHelper.e(标记“扫描失败:应用程序已启动具有相同设置的可恢复扫描”);
打破
案例扫描\失败\申请\注册\失败:
LogHelper.e(标记“扫描失败:应用程序无法注册”);
打破
案例扫描\u失败\u功能\u不受支持:
LogHelper.e(标记“扫描失败:不支持电源优化扫描功能”);
打破
案例扫描\u失败\u内部\u错误:
LogHelper.e(标记“扫描失败:内部错误”);
打破
违约:
LogHelper.e(标记,“扫描失败,错误未知(errorCode=“+errorCode+”));
打破
}
}
};
}
返回lescallback;
}

筛选“您的”信标的一般方法是查看所有信标共用的标识符前缀。然后,通过筛选与此标识符前缀匹配的信标来判断它是否是您的信标

执行过滤的两种方法:

A) 扫描结果输入后的软件过滤

使用这种方法,您需要等待解析信标,然后使用if语句查看信标标识符是否与前缀匹配。如果没有,请不要处理它。Android信标库将此作为一个内置功能,它使用区域对象为“您的”信标提供匹配模式

    // replace uuid with your own
    beaconManager.startRangingBeaconsInRegion(new Region("matchOnlyMyBeacons", Identifier.parse(“2F234454-CF6D-4A0F-ADF2-F4911BA9”)), null, null));

    beaconManager.addRangeNotifier(new RangeNotifier() {
        @Override
        public void didRangeBeaconsInRegion(Collection<Beacon> beacons, Region region) {
           // only beacons matching the identifiers in the Region are included here
        }
    });
这是一种非常灵活的简单方法。如果你的应用程序只在前台或后台运行,而周围很少有不感兴趣的可移动设备,那么它就可以很好地工作

缺点是,如果周围有很多不感兴趣的信标,它可能会消耗cpu和电池

B) 使用硬件扫描过滤器

Android 6+API允许您将类似的匹配功能放入蓝牙芯片本身,以便您获得的所有扫描回调都已匹配标识符前缀。这对CPU和电池的消耗较少,但也有缺点:

  • 并非所有设备都支持这一点,尽管2018年以来生产的大多数设备都支持这一点

  • 硬件过滤器是有限的资源。如果其他应用程序将它们全部占用,您将无法获得扫描结果

  • 过滤器不灵活。如果广告前缀的单个字节都不匹配(通常是由于制造商代码不同),您将无法获得扫描结果

     ScanFilter.Builder builder = new ScanFilter.Builder();
     builder.setServiceUuid(null);
     byte[] filterBytes = new byte[]{
             /* 0215 are the start of iBeacon.  Use beac for AltBeacon */
             (byte) 0x02, (byte) 0x15,
             // These bytes are your 16 byte proximityUUID (ID1)
             (byte) 0x2F, (byte) 0x23, (byte) 0x44, (byte) 0x54, (byte) 0xCF, (byte) 0x6D, (byte) 0x4A, (byte) 0x0F, (byte) 0xAD, (byte) 0xF2, (byte) 0xF4, (byte) 0x91, (byte) 0x1B, (byte) 0xA9, (byte) 0xFF, (byte) 0xA6
     };
    
     byte[] maskBytes = new byte[]{
             /* Make this the same length as your filter bytes, and set every value to 0xff to match all bytes */
             (byte) 0xff, (byte) 0xff,
             (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff
     };
     builder.setManufacturerData((int) 0x004c /* apple for iBeacon, use 0x0118 for AltBeacon */, filterBytes, maskBytes);
     ScanFilter[] scanFilters = new ScanFilter[] { builder.build() };
     scanner.startScan(scanFilters, scanSettings, scanCallback);
    

  • 你能出示你的扫描码吗?这是一个重要的部分,你可以限制被跟踪的内容。@davidgyoung我正在扫描设备,然后相应地进入
     ScanFilter.Builder builder = new ScanFilter.Builder();
     builder.setServiceUuid(null);
     byte[] filterBytes = new byte[]{
             /* 0215 are the start of iBeacon.  Use beac for AltBeacon */
             (byte) 0x02, (byte) 0x15,
             // These bytes are your 16 byte proximityUUID (ID1)
             (byte) 0x2F, (byte) 0x23, (byte) 0x44, (byte) 0x54, (byte) 0xCF, (byte) 0x6D, (byte) 0x4A, (byte) 0x0F, (byte) 0xAD, (byte) 0xF2, (byte) 0xF4, (byte) 0x91, (byte) 0x1B, (byte) 0xA9, (byte) 0xFF, (byte) 0xA6
     };
    
     byte[] maskBytes = new byte[]{
             /* Make this the same length as your filter bytes, and set every value to 0xff to match all bytes */
             (byte) 0xff, (byte) 0xff,
             (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff
     };
     builder.setManufacturerData((int) 0x004c /* apple for iBeacon, use 0x0118 for AltBeacon */, filterBytes, maskBytes);
     ScanFilter[] scanFilters = new ScanFilter[] { builder.build() };
     scanner.startScan(scanFilters, scanSettings, scanCallback);