/**
  * @see Beacon#mBluetoothAddress
  * @param bluetoothAddress
  * @return builder
  */
 public Builder setBluetoothAddress(String bluetoothAddress) {
   mBeacon.mBluetoothAddress = bluetoothAddress;
   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;
  }