Made the containers parcelable

This commit is contained in:
Alexandros Schillings
2014-03-10 13:07:56 +00:00
parent cc18c86b80
commit 827e3ba19c
4 changed files with 206 additions and 64 deletions
@@ -1,16 +1,21 @@
package uk.co.alt236.btlescan.containers; package uk.co.alt236.btlescan.containers;
import java.util.Arrays; import java.util.Arrays;
import android.os.Bundle;
import android.os.Parcel;
import android.os.Parcelable;
/** /**
* Created by Dave Smith * Created by Dave Smith
* Double Encore, Inc. * Double Encore, Inc.
* AdRecord *
* Expanded by Alexandros Schillings
*/ */
public final class AdRecord { public final class AdRecord implements Parcelable{
// 02 # Number of bytes that follow in first AD structure // 02 # Number of bytes that follow in first AD structure
// 01 # Flags AD type // 01 # Flags AD type
// 1A # Flags value 0x1A = 000011010 // 1A # Flags value 0x1A = 000011010
// bit 0 (OFF) LE Limited Discoverable Mode // bit 0 (OFF) LE Limited Discoverable Mode
// bit 1 (ON) LE General Discoverable Mode // bit 1 (ON) LE General Discoverable Mode
// bit 2 (OFF) BR/EDR Not Supported // bit 2 (OFF) BR/EDR Not Supported
@@ -22,16 +27,16 @@ public final class AdRecord {
// 02 # Byte 0 of iBeacon advertisement indicator // 02 # Byte 0 of iBeacon advertisement indicator
// 15 # Byte 1 of iBeacon advertisement indicator // 15 # Byte 1 of iBeacon advertisement indicator
// e2 c5 6d b5 df fb 48 d2 b0 60 d0 f5 a7 10 96 e0 # iBeacon proximity uuid // e2 c5 6d b5 df fb 48 d2 b0 60 d0 f5 a7 10 96 e0 # iBeacon proximity uuid
// 00 00 # major // 00 00 # major
// 00 00 # minor // 00 00 # minor
// c5 # The 2's complement of the calibrated Tx Power // c5 # The 2's complement of the calibrated Tx Power
/** /**
* General FLAGS * General FLAGS
* *
* Description: Flags * Description: Flags
* *
* Information: * Information:
* Bit 0: LE Limited Discoverable Mode * Bit 0: LE Limited Discoverable Mode
* Bit 1: LE General Discoverable Mode * Bit 1: LE General Discoverable Mode
@@ -67,9 +72,9 @@ public final class AdRecord {
/* SECURITY MANAGER OOB FLAGS /* SECURITY MANAGER OOB FLAGS
* *
* Description: Flag (1 octet) * Description: Flag (1 octet)
* *
* Information: * Information:
* Bit 0: OOB Flags Field: (0 = OOB data not present, 1 = OOB data present) * Bit 0: OOB Flags Field: (0 = OOB data not present, 1 = OOB data present)
* Bit 1: LE supported (Host) (i.e. bit 65 of LMP Extended Feature bits Page 1 * Bit 1: LE supported (Host) (i.e. bit 65 of LMP Extended Feature bits Page 1
@@ -81,10 +86,10 @@ public final class AdRecord {
/* SLAVE CONNECTION INTERVAL RANGE /* SLAVE CONNECTION INTERVAL RANGE
* *
* Description: Slave Connection Interval Range * Description: Slave Connection Interval Range
* *
* Information: * Information:
* The first 2 octets defines the minimum value for the connection interval in the following manner: * The first 2 octets defines the minimum value for the connection interval in the following manner:
* connInterval min = Conn_Interval_Min * 1.25 ms * connInterval min = Conn_Interval_Min * 1.25 ms
* Conn_Interval_Min range: 0x0006 to 0x0C80 * Conn_Interval_Min range: 0x0006 to 0x0C80
@@ -106,7 +111,7 @@ public final class AdRecord {
public static final int TYPE_SERVICE_UUIDS_LIST_128BIT = 0x15; public static final int TYPE_SERVICE_UUIDS_LIST_128BIT = 0x15;
/* SERVICE DATA /* SERVICE DATA
* *
* Description: Service Data (2 or more octets) * Description: Service Data (2 or more octets)
* Information: The first 2 octets contain the 16 bit Service UUID followed by additional service data * Information: The first 2 octets contain the 16 bit Service UUID followed by additional service data
*/ */
@@ -121,16 +126,39 @@ public final class AdRecord {
public static final int TYPE_MANUFACTURER_SPECIFIC_DATA = 0xFF; public static final int TYPE_MANUFACTURER_SPECIFIC_DATA = 0xFF;
/* Model Object Definition */ /* Model Object Definition */
private final int mLength; private final int mLength;
private final int mType; private final int mType;
private final byte[] mData; private final byte[] mData;
public static final Parcelable.Creator<AdRecord> CREATOR = new Parcelable.Creator<AdRecord>() {
public AdRecord createFromParcel(Parcel in) {
return new AdRecord(in);
}
public AdRecord[] newArray(int size) {
return new AdRecord[size];
}
};
public AdRecord(int length, int type, byte[] data) { public AdRecord(int length, int type, byte[] data) {
mLength = length; mLength = length;
mType = type; mType = type;
mData = data; mData = data;
} }
public AdRecord(Parcel in) {
final Bundle b = in.readBundle();
mLength = b.getInt("record_length");
mType = b.getInt("record_type");
mData = b.getByteArray("record_data");
}
@Override
public int describeContents() {
return 0;
}
public byte[] getData(){ public byte[] getData(){
return mData; return mData;
} }
@@ -142,16 +170,27 @@ public final class AdRecord {
public int getLength() { public int getLength() {
return mLength; return mLength;
} }
public int getType() { public int getType() {
return mType; return mType;
} }
@Override @Override
public String toString() { public String toString() {
return "AdRecord [mLength=" + mLength + ", mType=" + mType + ", mData=" + Arrays.toString(mData) + ", getHumanReadableType()=" + getHumanReadableType() + "]"; return "AdRecord [mLength=" + mLength + ", mType=" + mType + ", mData=" + Arrays.toString(mData) + ", getHumanReadableType()=" + getHumanReadableType() + "]";
} }
@Override
public void writeToParcel(Parcel parcel, int arg1) {
final Bundle b = new Bundle();
b.putInt("record_length", mLength);
b.putInt("record_type", mType);
b.putByteArray("record_data", mData);
parcel.writeBundle(b);
}
private static String getHumanReadableAdType(int type){ private static String getHumanReadableAdType(int type){
switch(type){ switch(type){
case TYPE_CONNECTION_INTERVAL_RANGE: case TYPE_CONNECTION_INTERVAL_RANGE:
@@ -1,59 +1,101 @@
package uk.co.alt236.btlescan.containers; package uk.co.alt236.btlescan.containers;
import java.util.ArrayList;
import java.util.Collection; import java.util.Collection;
import java.util.Collections; import java.util.Collections;
import java.util.Map;
public class AdRecordStore { import android.os.Bundle;
private final Map<Integer, AdRecord> mAdRecords; import android.os.Parcel;
private final int mServiceDataUUId; import android.os.Parcelable;
import android.util.SparseArray;
public class AdRecordStore implements Parcelable{
private final SparseArray<AdRecord> mAdRecords;
private final String mLocalNameComplete; private final String mLocalNameComplete;
private final String mLocalNameShort; private final String mLocalNameShort;
public AdRecordStore(Map<Integer, AdRecord> adRecords){ public static final Parcelable.Creator<AdRecordStore> CREATOR = new Parcelable.Creator<AdRecordStore>() {
public AdRecordStore createFromParcel(Parcel in) {
return new AdRecordStore(in);
}
public AdRecordStore[] newArray(int size) {
return new AdRecordStore[size];
}
};
public AdRecordStore(Parcel in) {
final Bundle b = in.readBundle();
mAdRecords = b.getSparseParcelableArray("records_array");
mLocalNameComplete = b.getString("local_name_complete");
mLocalNameShort = b.getString("local_name_short");
}
public AdRecordStore(final SparseArray<AdRecord> adRecords){
mAdRecords = adRecords; mAdRecords = adRecords;
mServiceDataUUId = AdRecordUtils.getServiceDataUuid(
mAdRecords.get(AdRecord.TYPE_SERVICE_DATA));
mLocalNameComplete = AdRecordUtils.getRecordDataAsString( mLocalNameComplete = AdRecordUtils.getRecordDataAsString(
mAdRecords.get(AdRecord.TYPE_LOCAL_NAME_COMPLETE)); mAdRecords.get(AdRecord.TYPE_LOCAL_NAME_COMPLETE));
mLocalNameShort = AdRecordUtils.getRecordDataAsString( mLocalNameShort = AdRecordUtils.getRecordDataAsString(
mAdRecords.get(AdRecord.TYPE_LOCAL_NAME_SHORT)); mAdRecords.get(AdRecord.TYPE_LOCAL_NAME_SHORT));
} }
@Override
public int describeContents() {
return 0;
}
public String getLocalNameComplete() { public String getLocalNameComplete() {
return mLocalNameComplete; return mLocalNameComplete;
} }
public String getLocalNameShort() { public String getLocalNameShort() {
return mLocalNameShort; return mLocalNameShort;
} }
public AdRecord getRecord(int record){ public AdRecord getRecord(int record){
return mAdRecords.get(record); return mAdRecords.get(record);
} }
public String getRecordDataAsString(int record){ public String getRecordDataAsString(int record){
return AdRecordUtils.getRecordDataAsString( return AdRecordUtils.getRecordDataAsString(
mAdRecords.get(record)); mAdRecords.get(record));
} }
public int getServiceDataUUID(){ public Collection<AdRecord> getRecordsAsCollection() {
return mServiceDataUUId; return Collections.unmodifiableCollection(asList(mAdRecords));
} }
public boolean isRecordPresent(int record){ public boolean isRecordPresent(int record){
return mAdRecords.containsKey(record); return mAdRecords.indexOfKey(record) >= 0;
} }
@Override @Override
public String toString() { public String toString() {
return "AdRecordStore [mServiceDataUUId=" + mServiceDataUUId + ", mLocalNameComplete=" + mLocalNameComplete + ", mLocalNameShort=" + mLocalNameShort + "]"; return "AdRecordStore [mLocalNameComplete=" + mLocalNameComplete + ", mLocalNameShort=" + mLocalNameShort + "]";
} }
public Collection<AdRecord> getRecordsAsCollection() { @Override
return Collections.unmodifiableCollection(mAdRecords.values()); public void writeToParcel(Parcel parcel, int arg1) {
final Bundle b = new Bundle();
b.putString("local_name_complete", mLocalNameComplete);
b.putString("local_name_short", mLocalNameShort);
b.putSparseParcelableArray("records_array", mAdRecords);
parcel.writeBundle(b);
}
public static <C> Collection<C> asList(SparseArray<C> sparseArray) {
if (sparseArray == null) return null;
final Collection<C> arrayList = new ArrayList<C>(sparseArray.size());
for (int i = 0; i < sparseArray.size(); i++){
arrayList.add(sparseArray.valueAt(i));
}
return arrayList;
} }
} }
@@ -8,6 +8,7 @@ import java.util.List;
import java.util.Map; import java.util.Map;
import android.annotation.SuppressLint; import android.annotation.SuppressLint;
import android.util.SparseArray;
public class AdRecordUtils { public class AdRecordUtils {
/* Helper functions to parse out common data payloads from an AD structure */ /* Helper functions to parse out common data payloads from an AD structure */
@@ -108,4 +109,29 @@ public class AdRecordUtils {
return Collections.unmodifiableMap(records); return Collections.unmodifiableMap(records);
} }
public static SparseArray<AdRecord> parseScanRecordAsSparseArray(byte[] scanRecord) {
final SparseArray<AdRecord> records = new SparseArray<AdRecord>();
int index = 0;
while (index < scanRecord.length) {
final int length = scanRecord[index++];
//Done once we run out of records
if (length == 0) break;
int type = scanRecord[index];
//Done if our record isn't a valid type
if (type == 0) break;
final byte[] data = Arrays.copyOfRange(scanRecord, index+1, index+length);
records.put(type, new AdRecord(length, type, data));
//Advance
index += length;
}
return records;
}
} }
@@ -4,20 +4,46 @@ import java.util.Arrays;
import android.bluetooth.BluetoothClass; import android.bluetooth.BluetoothClass;
import android.bluetooth.BluetoothDevice; import android.bluetooth.BluetoothDevice;
import android.os.Bundle;
import android.os.Parcel;
import android.os.Parcelable;
public class BluetoothLeDevice { public class BluetoothLeDevice implements Parcelable{
private final BluetoothDevice mDevice;
private transient final int mRssi;
private final byte[] mScanRecord;
private final AdRecordStore mRecordStore; private final AdRecordStore mRecordStore;
private final BluetoothDevice mDevice;
private final byte[] mScanRecord;
private transient final int mRssi;
public static final Parcelable.Creator<BluetoothLeDevice> CREATOR = new Parcelable.Creator<BluetoothLeDevice>() {
public BluetoothLeDevice createFromParcel(Parcel in) {
return new BluetoothLeDevice(in);
}
public BluetoothLeDevice[] newArray(int size) {
return new BluetoothLeDevice[size];
}
};
public BluetoothLeDevice(BluetoothDevice device, int rssi, byte[] scanRecord){ public BluetoothLeDevice(BluetoothDevice device, int rssi, byte[] scanRecord){
mDevice = device; mDevice = device;
mRssi = rssi; mRssi = rssi;
mScanRecord = scanRecord; mScanRecord = scanRecord;
mRecordStore = new AdRecordStore(AdRecordUtils.parseScanRecordAsMap(scanRecord)); mRecordStore = new AdRecordStore(AdRecordUtils.parseScanRecordAsSparseArray(scanRecord));
} }
private BluetoothLeDevice(Parcel in) {
final Bundle b = in.readBundle();
mDevice = b.getParcelable("bluetooth_device");
mRecordStore = b.getParcelable("device_scanrecord_store");
mRssi = b.getInt("device_rssi");
mScanRecord = b.getByteArray("device_scanrecord");
}
@Override
public int describeContents() {
return 0;
}
@Override @Override
public boolean equals(Object obj) { public boolean equals(Object obj) {
if (this == obj) if (this == obj)
@@ -36,11 +62,11 @@ public class BluetoothLeDevice {
return false; return false;
return true; return true;
} }
public String getAddress(){ public String getAddress(){
return mDevice.getAddress(); return mDevice.getAddress();
} }
public AdRecordStore getAdRecordStore(){ public AdRecordStore getAdRecordStore(){
return mRecordStore; return mRecordStore;
} }
@@ -52,15 +78,15 @@ public class BluetoothLeDevice {
public String getBluetoothDeviceClassName(){ public String getBluetoothDeviceClassName(){
return resolveBluetoothClass(mDevice.getBluetoothClass().getDeviceClass()); return resolveBluetoothClass(mDevice.getBluetoothClass().getDeviceClass());
} }
public BluetoothDevice getDevice() { public BluetoothDevice getDevice() {
return mDevice; return mDevice;
} }
public String getName(){ public String getName(){
return mDevice.getName(); return mDevice.getName();
} }
public int getRssi() { public int getRssi() {
return mRssi; return mRssi;
} }
@@ -77,19 +103,28 @@ public class BluetoothLeDevice {
result = prime * result + Arrays.hashCode(mScanRecord); result = prime * result + Arrays.hashCode(mScanRecord);
return result; return result;
} }
@Override @Override
public String toString() { public String toString() {
return "BluetoothLeDevice [mDevice=" + mDevice + ", mRssi=" + mRssi + ", mScanRecord=" + AdRecordUtils.byteArrayToHexString(mScanRecord) + ", mRecordStore=" + mRecordStore + ", getBluetoothDeviceBondState()=" + getBluetoothDeviceBondState() + ", getBluetoothDeviceClassName()=" + getBluetoothDeviceClassName() + "]"; return "BluetoothLeDevice [mDevice=" + mDevice + ", mRssi=" + mRssi + ", mScanRecord=" + AdRecordUtils.byteArrayToHexString(mScanRecord) + ", mRecordStore=" + mRecordStore + ", getBluetoothDeviceBondState()=" + getBluetoothDeviceBondState() + ", getBluetoothDeviceClassName()=" + getBluetoothDeviceClassName() + "]";
} }
@Override
public void writeToParcel(Parcel parcel, int arg1) {
final Bundle b = new Bundle();
b.putByteArray("device_scanrecord", mScanRecord);
b.putInt("device_rssi", mRssi);
b.putParcelable("bluetooth_device", mDevice);
b.putParcelable("device_scanrecord_store", mRecordStore);
parcel.writeBundle(b);
}
private static String resolveBluetoothClass(int btClass){ private static String resolveBluetoothClass(int btClass){
switch (btClass){ switch (btClass){
case BluetoothClass.Device.AUDIO_VIDEO_CAMCORDER: case BluetoothClass.Device.AUDIO_VIDEO_CAMCORDER:
return "A/V, Camcorder"; return "A/V, Camcorder";
case BluetoothClass.Device.AUDIO_VIDEO_CAR_AUDIO: case BluetoothClass.Device.AUDIO_VIDEO_CAR_AUDIO:
return "A/V, Car Audio"; return "A/V, Car Audio";
case BluetoothClass.Device.AUDIO_VIDEO_HANDSFREE: case BluetoothClass.Device.AUDIO_VIDEO_HANDSFREE:
return "A/V, Handsfree"; return "A/V, Handsfree";
@@ -111,7 +146,7 @@ public class BluetoothLeDevice {
return "A/V, VCR"; return "A/V, VCR";
case BluetoothClass.Device.AUDIO_VIDEO_VIDEO_CAMERA: case BluetoothClass.Device.AUDIO_VIDEO_VIDEO_CAMERA:
return "A/V, Video Camera"; return "A/V, Video Camera";
case BluetoothClass.Device.AUDIO_VIDEO_VIDEO_CONFERENCING: case BluetoothClass.Device.AUDIO_VIDEO_VIDEO_CONFERENCING:
return "A/V, Video Conferencing"; return "A/V, Video Conferencing";
case BluetoothClass.Device.AUDIO_VIDEO_VIDEO_DISPLAY_AND_LOUDSPEAKER: case BluetoothClass.Device.AUDIO_VIDEO_VIDEO_DISPLAY_AND_LOUDSPEAKER:
return "A/V, Video Display and Loudspeaker"; return "A/V, Video Display and Loudspeaker";
@@ -135,17 +170,17 @@ public class BluetoothLeDevice {
return "Computer, Uncategorized"; return "Computer, Uncategorized";
case BluetoothClass.Device.COMPUTER_WEARABLE: case BluetoothClass.Device.COMPUTER_WEARABLE:
return "Computer, Wearable"; return "Computer, Wearable";
case BluetoothClass.Device.HEALTH_BLOOD_PRESSURE: case BluetoothClass.Device.HEALTH_BLOOD_PRESSURE:
return "Health, Blood Pressure"; return "Health, Blood Pressure";
case BluetoothClass.Device.HEALTH_DATA_DISPLAY: case BluetoothClass.Device.HEALTH_DATA_DISPLAY:
return "Health, Data Display"; return "Health, Data Display";
case BluetoothClass.Device.HEALTH_GLUCOSE: case BluetoothClass.Device.HEALTH_GLUCOSE:
return "Health, Glucose"; return "Health, Glucose";
case BluetoothClass.Device.HEALTH_PULSE_OXIMETER : case BluetoothClass.Device.HEALTH_PULSE_OXIMETER :
return "Health, Pulse Oximeter"; return "Health, Pulse Oximeter";
case BluetoothClass.Device.HEALTH_PULSE_RATE : case BluetoothClass.Device.HEALTH_PULSE_RATE :
return "Health, Pulse Rate"; return "Health, Pulse Rate";
case BluetoothClass.Device.HEALTH_THERMOMETER : case BluetoothClass.Device.HEALTH_THERMOMETER :
return "Health, Thermometer"; return "Health, Thermometer";
case BluetoothClass.Device.HEALTH_UNCATEGORIZED : case BluetoothClass.Device.HEALTH_UNCATEGORIZED :
return "Health, Uncategorized"; return "Health, Uncategorized";
@@ -177,9 +212,9 @@ public class BluetoothLeDevice {
return "Toy, Vehicle"; return "Toy, Vehicle";
case BluetoothClass.Device.WEARABLE_GLASSES: case BluetoothClass.Device.WEARABLE_GLASSES:
return "Wearable, Glasses"; return "Wearable, Glasses";
case BluetoothClass.Device.WEARABLE_HELMET: case BluetoothClass.Device.WEARABLE_HELMET:
return "Wearable, Helmet"; return "Wearable, Helmet";
case BluetoothClass.Device.WEARABLE_JACKET: case BluetoothClass.Device.WEARABLE_JACKET:
return "Wearable, Jacket"; return "Wearable, Jacket";
case BluetoothClass.Device.WEARABLE_PAGER: case BluetoothClass.Device.WEARABLE_PAGER:
return "Wearable, Pager"; return "Wearable, Pager";
@@ -191,7 +226,7 @@ public class BluetoothLeDevice {
return "Unknown, Unknown (class=" + btClass +")"; return "Unknown, Unknown (class=" + btClass +")";
} }
} }
private static String resolveBondingState(int bondState){ private static String resolveBondingState(int bondState){
switch (bondState){ switch (bondState){
case BluetoothDevice.BOND_BONDED: case BluetoothDevice.BOND_BONDED: