Я работаю над решением, которое рекламирует и сканирует в формате iBeacon с использованием библиотеки AltBeacon . Меня беспокоит то, что библиотека сканирует все устройства, и это нормально, но после анализа отсканированных устройств она также отслеживает рекламные устройства, которые не рекламируются из моего приложения. Есть ли способ решить эту проблему с помощью библиотеки? Если нет, то что может быть альтернативным решением этой проблемы. Для меня очень важно отслеживать рекламные маяки, которые являются рекламой только из моего приложения.
Это код, который используется при рекламе в формате iBeacon через библиотеку AltBeacon:
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;
}