/** * @see Beacon#mRssi * @param rssi * @return builder */ public Builder setRssi(int rssi) { mBeacon.mRssi = rssi; return this; }
@TargetApi(5) 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; if (LogManager.isVerboseLoggingEnabled()) { LogManager.d( TAG, "Processing pdu type %02X: %s with startIndex: %d, endIndex: %d", pdu.getType(), bytesToHex(bytesToProcess), pdu.getStartIndex(), pdu.getEndIndex()); } break; } else { if (LogManager.isVerboseLoggingEnabled()) { LogManager.d(TAG, "Ignoring pdu type %02X", pdu.getType()); } } } if (pduToParse == null) { if (LogManager.isVerboseLoggingEnabled()) { LogManager.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, 0)) { patternFound = true; } } else { if (byteArraysMatch( bytesToProcess, startByte + mServiceUuidStartOffset, serviceUuidBytes, 0) && byteArraysMatch( bytesToProcess, startByte + mMatchingBeaconTypeCodeStartOffset, typeCodeBytes, 0)) { patternFound = true; } } if (patternFound == false) { // This is not a beacon if (getServiceUuid() == null) { if (LogManager.isVerboseLoggingEnabled()) { LogManager.d( TAG, "This is not a matching Beacon advertisement. (Was expecting %s. " + "The bytes I see are: %s", byteArrayToString(typeCodeBytes), bytesToHex(bytesToProcess)); } } else { if (LogManager.isVerboseLoggingEnabled()) { LogManager.d( TAG, "This is not a matching Beacon advertisement. Was expecting %s at offset %d and %s at offset %d. " + "The bytes I see are: %s", byteArrayToString(serviceUuidBytes), startByte + mServiceUuidStartOffset, byteArrayToString(typeCodeBytes), startByte + mMatchingBeaconTypeCodeStartOffset, bytesToHex(bytesToProcess)); } } parseFailed = true; beacon = null; } else { if (LogManager.isVerboseLoggingEnabled()) { LogManager.d( TAG, "This is a recognized beacon advertisement -- %s seen", byteArrayToString(typeCodeBytes)); } } 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 if (LogManager.isVerboseLoggingEnabled()) { LogManager.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)) { if (LogManager.isVerboseLoggingEnabled()) { LogManager.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 Identifier identifier = Identifier.fromBytes( bytesToProcess, mIdentifierStartOffsets.get(i) + startByte, pduToParse.getEndIndex() + 1, mIdentifierLittleEndianFlags.get(i)); identifiers.add(identifier); } else if (endIndex > pduToParse.getEndIndex() && !mAllowPduOverflow) { parseFailed = true; if (LogManager.isVerboseLoggingEnabled()) { LogManager.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) { if (LogManager.isVerboseLoggingEnabled()) { LogManager.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.parseLong(dataString)); } } if (mPowerStartOffset != null) { int endIndex = mPowerEndOffset + startByte; int txPower = 0; try { if (endIndex > pduToParse.getEndIndex() && !mAllowPduOverflow) { parseFailed = true; if (LogManager.isVerboseLoggingEnabled()) { LogManager.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; } return beacon; }