package com.idevicesinc.sweetblue.utils; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.UUID; /** * Class used to store information from a BLE scan record. This class can also be used to create a scan record. */ public final class BleScanInfo implements UsesCustomNull { public final static BleScanInfo NULL = new BleScanInfo(); private Short m_manufactuerId; private byte[] m_manufacturerData; private Pointer<Integer> m_advFlags; private Pointer<Integer> m_txPower; private final List<BleUuid> m_serviceUuids; private final Map<UUID, byte[]> m_serviceData; private boolean m_completeUuidList; private String m_localName; private boolean m_shortName; /** * Basic constructor to use if you are building a scan record to advertise. */ public BleScanInfo() { m_serviceUuids = new ArrayList<>(); m_serviceData = new HashMap<>(); m_completeUuidList = false; } /** * Constructor used internally when a {@link com.idevicesinc.sweetblue.BleDevice} is discovered. */ public BleScanInfo(Pointer<Integer> advFlags, Pointer<Integer> txPower, List<UUID> serviceUuids, boolean uuidCompleteList, short mfgId, byte[] mfgData, Map<UUID, byte[]> serviceData, String localName, boolean shortName) { m_advFlags = advFlags; m_txPower = txPower; m_serviceUuids = new ArrayList<>(); addServiceUUIDs(serviceUuids); m_manufactuerId = mfgId; m_manufacturerData = mfgData; if (serviceData == null) { m_serviceData = new HashMap<>(0); } else { m_serviceData = serviceData; } m_localName = localName; m_shortName = shortName; m_completeUuidList = uuidCompleteList; } /** * Clear all service data that may be in this {@link BleScanInfo} instance. * See also {@link #clearServiceUUIDs()}. */ public final BleScanInfo clearServiceData() { m_serviceData.clear(); return this; } /** * Set the service data for this {@link BleScanInfo} instance. */ public final BleScanInfo addServiceData(Map<UUID, byte[]> data) { m_serviceData.putAll(data); return this; } /** * Clears any service UUIDs in this instance. */ public final BleScanInfo clearServiceUUIDs() { m_serviceUuids.clear(); return this; } /** * Add the given List of {@link UUID}s to this instance's UUID list. */ public final BleScanInfo addServiceUUIDs(List<UUID> uuids) { if (uuids != null) { for (UUID u : uuids) { BleUuid.UuidSize size = shortUuid(u) ? BleUuid.UuidSize.SHORT : BleUuid.UuidSize.FULL; m_serviceUuids.add(new BleUuid(u, size)); } } return this; } /** * Add the given {@link UUID} and data to this instance's service data map. */ public final BleScanInfo addServiceData(UUID uuid, byte[] data) { m_serviceData.put(uuid, data); return this; } /** * Add a {@link UUID} with the given {@link BleUuid.UuidSize} to this instance's {@link UUID} list. */ public final BleScanInfo addServiceUuid(UUID uuid, BleUuid.UuidSize size) { m_serviceUuids.add(new BleUuid(uuid, size)); return this; } /** * Overload of {@link #addServiceUuid(UUID, BleUuid.UuidSize)}, which sets the size to {@link BleUuid.UuidSize#SHORT}, if it can fit, otherwise it will * default to {@link BleUuid.UuidSize#FULL} */ public final BleScanInfo addServiceUuid(UUID uuid) { final BleUuid.UuidSize size = shortUuid(uuid) ? BleUuid.UuidSize.SHORT : BleUuid.UuidSize.FULL; return addServiceUuid(uuid, size); } /** * Set the manufacturer Id */ public final BleScanInfo setManufacturerId(short id) { m_manufactuerId = id; return this; } /** * Set the manufacturer data */ public final BleScanInfo setManufacturerData(byte[] data) { m_manufacturerData = data; return this; } /** * Overload of {@link #setName(String, boolean)}, which defaults to a complete name (not short). */ public final BleScanInfo setName(String name) { return setName(name, false); } /** * Set the device name, and if it's a shortened name or not. */ public final BleScanInfo setName(String name, boolean shortName) { m_localName = name; m_shortName = shortName; return this; } /** * Get the manufacturer Id from this {@link BleScanInfo} instance. */ public final short getManufacturerId() { if (m_manufactuerId == null) { return -1; } return m_manufactuerId; } /** * Get the manufacturer data from this instance. */ public final byte[] getManufacturerData() { if (m_manufacturerData == null) { return P_Const.EMPTY_BYTE_ARRAY; } return m_manufacturerData; } /** * Set the advertising flags. This method expects a byte bitmask (so all flags are already OR'd). */ public final BleScanInfo setAdvFlags(byte mask) { if (m_advFlags == null) { m_advFlags = new Pointer<>((int) mask); } else { m_advFlags.value = (int) mask; } return this; } /** * Convenience method to set the advertising flags, which allows you to pass in every flag you want, and this * method will OR them together for you. */ public final BleScanInfo setAdvFlags(byte... flags) { if (flags == null || flags.length == 0) { return this; } if (m_advFlags == null) { m_advFlags = new Pointer<>(0); } for (byte b : flags) { m_advFlags.value |= b; } return this; } /** * Get the advertising flags for this instance. */ public final Pointer<Integer> getAdvFlags() { if (m_advFlags == null) { return new Pointer<>(0); } return m_advFlags; } /** * Set the TX power */ public final BleScanInfo setTxPower(byte power) { if (m_txPower == null) { m_txPower = new Pointer<>((int) power); } else { m_txPower.value = (int) power; } return this; } /** * Gets the Tx power */ public final Pointer<Integer> getTxPower() { if (m_txPower == null) { return new Pointer<>(0); } return m_txPower; } /** * Returns a list of service {@link UUID}s. This ONLY includes {@link UUID}s that do NOT have any data associated with them. * * See also {@link #getServiceData()}. */ public final List<UUID> getServiceUUIDS() { List<UUID> list = new ArrayList<>(); if (m_serviceUuids != null) { for (BleUuid u : m_serviceUuids) { list.add(u.uuid()); } } return list; } /** * Returns a {@link Map} of the service data in this instance. * * See also {@link #getServiceUUIDS()}. */ public final Map<UUID, byte[]> getServiceData() { return m_serviceData; } /** * Returns the device name */ public final String getName() { if (m_localName == null) { return ""; } return m_localName; } /** * Returns whether the name is a shortened version or not. */ public final boolean isShortName() { return m_shortName; } /** * Returns <code>true</code> if this instance is considered null. */ @Override public final boolean isNull() { return this == NULL; } /** * Build a byte[] scan record from the data stored in this instance. */ public final byte[] buildPacket() { Map<BleUuid, byte[]> map = new HashMap<>(m_serviceUuids.size() + m_serviceData.size()); if (m_serviceUuids.size() > 0) { for (BleUuid u : m_serviceUuids) { map.put(u, null); } } if (m_serviceData.size() > 0) { for (UUID u : m_serviceData.keySet()) { map.put(new BleUuid(u, BleUuid.UuidSize.SHORT), m_serviceData.get(u)); } } return Utils_ScanRecord.newScanRecord(m_advFlags.value.byteValue(), map, m_completeUuidList, m_localName, m_shortName, m_txPower.value.byteValue(), m_manufactuerId, m_manufacturerData); } private static boolean shortUuid(UUID u) { long msb = u.getMostSignificantBits(); short m = (short) (msb >>> 32); UUID test = Uuids.fromShort(Utils_Byte.bytesToHexString(Utils_Byte.shortToBytes(m))); return test.equals(u); } }