blob: 855d3454170722849489df2c6676bd28dc8181a5 [file] [log] [blame]
/*
* Copyright (C) 2014 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package android.bluetooth.le;
import android.annotation.Nullable;
import android.bluetooth.BluetoothDevice;
import android.os.Parcel;
import android.os.Parcelable;
import java.util.Objects;
/**
* ScanResult for Bluetooth LE scan.
*/
public final class ScanResult implements Parcelable {
/**
* For chained advertisements, inidcates tha the data contained in this
* scan result is complete.
*/
public static final int DATA_COMPLETE = 0x00;
/**
* For chained advertisements, indicates that the controller was
* unable to receive all chained packets and the scan result contains
* incomplete truncated data.
*/
public static final int DATA_TRUNCATED = 0x02;
/**
* Indicates that the secondary physical layer was not used.
*/
public static final int PHY_UNUSED = 0x00;
/**
* Advertising Set ID is not present in the packet.
*/
public static final int SID_NOT_PRESENT = 0xFF;
/**
* TX power is not present in the packet.
*/
public static final int TX_POWER_NOT_PRESENT = 0x7F;
/**
* Periodic advertising interval is not present in the packet.
*/
public static final int PERIODIC_INTERVAL_NOT_PRESENT = 0x00;
/**
* Mask for checking whether event type represents legacy advertisement.
*/
private static final int ET_LEGACY_MASK = 0x10;
/**
* Mask for checking whether event type represents connectable advertisement.
*/
private static final int ET_CONNECTABLE_MASK = 0x01;
// Remote Bluetooth device.
private BluetoothDevice mDevice;
// Scan record, including advertising data and scan response data.
@Nullable
private ScanRecord mScanRecord;
// Received signal strength.
private int mRssi;
// Device timestamp when the result was last seen.
private long mTimestampNanos;
private int mEventType;
private int mPrimaryPhy;
private int mSecondaryPhy;
private int mAdvertisingSid;
private int mTxPower;
private int mPeriodicAdvertisingInterval;
/**
* Constructs a new ScanResult.
*
* @param device Remote Bluetooth device found.
* @param scanRecord Scan record including both advertising data and scan response data.
* @param rssi Received signal strength.
* @param timestampNanos Timestamp at which the scan result was observed.
* @deprecated use {@link #ScanResult(BluetoothDevice, int, int, int, int, int, int, int,
* ScanRecord, long)}
*/
@Deprecated
public ScanResult(BluetoothDevice device, ScanRecord scanRecord, int rssi,
long timestampNanos) {
mDevice = device;
mScanRecord = scanRecord;
mRssi = rssi;
mTimestampNanos = timestampNanos;
mEventType = (DATA_COMPLETE << 5) | ET_LEGACY_MASK | ET_CONNECTABLE_MASK;
mPrimaryPhy = BluetoothDevice.PHY_LE_1M;
mSecondaryPhy = PHY_UNUSED;
mAdvertisingSid = SID_NOT_PRESENT;
mTxPower = 127;
mPeriodicAdvertisingInterval = 0;
}
/**
* Constructs a new ScanResult.
*
* @param device Remote Bluetooth device found.
* @param eventType Event type.
* @param primaryPhy Primary advertising phy.
* @param secondaryPhy Secondary advertising phy.
* @param advertisingSid Advertising set ID.
* @param txPower Transmit power.
* @param rssi Received signal strength.
* @param periodicAdvertisingInterval Periodic advertising interval.
* @param scanRecord Scan record including both advertising data and scan response data.
* @param timestampNanos Timestamp at which the scan result was observed.
*/
public ScanResult(BluetoothDevice device, int eventType, int primaryPhy, int secondaryPhy,
int advertisingSid, int txPower, int rssi, int periodicAdvertisingInterval,
ScanRecord scanRecord, long timestampNanos) {
mDevice = device;
mEventType = eventType;
mPrimaryPhy = primaryPhy;
mSecondaryPhy = secondaryPhy;
mAdvertisingSid = advertisingSid;
mTxPower = txPower;
mRssi = rssi;
mPeriodicAdvertisingInterval = periodicAdvertisingInterval;
mScanRecord = scanRecord;
mTimestampNanos = timestampNanos;
}
private ScanResult(Parcel in) {
readFromParcel(in);
}
@Override
public void writeToParcel(Parcel dest, int flags) {
if (mDevice != null) {
dest.writeInt(1);
mDevice.writeToParcel(dest, flags);
} else {
dest.writeInt(0);
}
if (mScanRecord != null) {
dest.writeInt(1);
dest.writeByteArray(mScanRecord.getBytes());
} else {
dest.writeInt(0);
}
dest.writeInt(mRssi);
dest.writeLong(mTimestampNanos);
dest.writeInt(mEventType);
dest.writeInt(mPrimaryPhy);
dest.writeInt(mSecondaryPhy);
dest.writeInt(mAdvertisingSid);
dest.writeInt(mTxPower);
dest.writeInt(mPeriodicAdvertisingInterval);
}
private void readFromParcel(Parcel in) {
if (in.readInt() == 1) {
mDevice = BluetoothDevice.CREATOR.createFromParcel(in);
}
if (in.readInt() == 1) {
mScanRecord = ScanRecord.parseFromBytes(in.createByteArray());
}
mRssi = in.readInt();
mTimestampNanos = in.readLong();
mEventType = in.readInt();
mPrimaryPhy = in.readInt();
mSecondaryPhy = in.readInt();
mAdvertisingSid = in.readInt();
mTxPower = in.readInt();
mPeriodicAdvertisingInterval = in.readInt();
}
@Override
public int describeContents() {
return 0;
}
/**
* Returns the remote Bluetooth device identified by the Bluetooth device address.
*/
public BluetoothDevice getDevice() {
return mDevice;
}
/**
* Returns the scan record, which is a combination of advertisement and scan response.
*/
@Nullable
public ScanRecord getScanRecord() {
return mScanRecord;
}
/**
* Returns the received signal strength in dBm. The valid range is [-127, 126].
*/
public int getRssi() {
return mRssi;
}
/**
* Returns timestamp since boot when the scan record was observed.
*/
public long getTimestampNanos() {
return mTimestampNanos;
}
/**
* Returns true if this object represents legacy scan result.
* Legacy scan results do not contain advanced advertising information
* as specified in the Bluetooth Core Specification v5.
*/
public boolean isLegacy() {
return (mEventType & ET_LEGACY_MASK) != 0;
}
/**
* Returns true if this object represents connectable scan result.
*/
public boolean isConnectable() {
return (mEventType & ET_CONNECTABLE_MASK) != 0;
}
/**
* Returns the data status.
* Can be one of {@link ScanResult#DATA_COMPLETE} or
* {@link ScanResult#DATA_TRUNCATED}.
*/
public int getDataStatus() {
// return bit 5 and 6
return (mEventType >> 5) & 0x03;
}
/**
* Returns the primary Physical Layer
* on which this advertisment was received.
* Can be one of {@link BluetoothDevice#PHY_LE_1M} or
* {@link BluetoothDevice#PHY_LE_CODED}.
*/
public int getPrimaryPhy() {
return mPrimaryPhy;
}
/**
* Returns the secondary Physical Layer
* on which this advertisment was received.
* Can be one of {@link BluetoothDevice#PHY_LE_1M},
* {@link BluetoothDevice#PHY_LE_2M}, {@link BluetoothDevice#PHY_LE_CODED}
* or {@link ScanResult#PHY_UNUSED} - if the advertisement
* was not received on a secondary physical channel.
*/
public int getSecondaryPhy() {
return mSecondaryPhy;
}
/**
* Returns the advertising set id.
* May return {@link ScanResult#SID_NOT_PRESENT} if
* no set id was is present.
*/
public int getAdvertisingSid() {
return mAdvertisingSid;
}
/**
* Returns the transmit power in dBm.
* Valid range is [-127, 126]. A value of {@link ScanResult#TX_POWER_NOT_PRESENT}
* indicates that the TX power is not present.
*/
public int getTxPower() {
return mTxPower;
}
/**
* Returns the periodic advertising interval in units of 1.25ms.
* Valid range is 6 (7.5ms) to 65536 (81918.75ms). A value of
* {@link ScanResult#PERIODIC_INTERVAL_NOT_PRESENT} means periodic
* advertising interval is not present.
*/
public int getPeriodicAdvertisingInterval() {
return mPeriodicAdvertisingInterval;
}
@Override
public int hashCode() {
return Objects.hash(mDevice, mRssi, mScanRecord, mTimestampNanos,
mEventType, mPrimaryPhy, mSecondaryPhy,
mAdvertisingSid, mTxPower,
mPeriodicAdvertisingInterval);
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null || getClass() != obj.getClass()) {
return false;
}
ScanResult other = (ScanResult) obj;
return Objects.equals(mDevice, other.mDevice) && (mRssi == other.mRssi)
&& Objects.equals(mScanRecord, other.mScanRecord)
&& (mTimestampNanos == other.mTimestampNanos)
&& mEventType == other.mEventType
&& mPrimaryPhy == other.mPrimaryPhy
&& mSecondaryPhy == other.mSecondaryPhy
&& mAdvertisingSid == other.mAdvertisingSid
&& mTxPower == other.mTxPower
&& mPeriodicAdvertisingInterval == other.mPeriodicAdvertisingInterval;
}
@Override
public String toString() {
return "ScanResult{" + "device=" + mDevice + ", scanRecord="
+ Objects.toString(mScanRecord) + ", rssi=" + mRssi
+ ", timestampNanos=" + mTimestampNanos + ", eventType=" + mEventType
+ ", primaryPhy=" + mPrimaryPhy + ", secondaryPhy=" + mSecondaryPhy
+ ", advertisingSid=" + mAdvertisingSid + ", txPower=" + mTxPower
+ ", periodicAdvertisingInterval=" + mPeriodicAdvertisingInterval + '}';
}
public static final @android.annotation.NonNull Parcelable.Creator<ScanResult> CREATOR = new Creator<ScanResult>() {
@Override
public ScanResult createFromParcel(Parcel source) {
return new ScanResult(source);
}
@Override
public ScanResult[] newArray(int size) {
return new ScanResult[size];
}
};
}