362 lines
11 KiB
Java
362 lines
11 KiB
Java
/*
|
|
* 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.NonNull;
|
|
import android.annotation.Nullable;
|
|
import android.bluetooth.Attributable;
|
|
import android.bluetooth.BluetoothDevice;
|
|
import android.content.AttributionSource;
|
|
import android.os.Parcel;
|
|
import android.os.Parcelable;
|
|
|
|
import java.util.Objects;
|
|
|
|
/**
|
|
* ScanResult for Bluetooth LE scan.
|
|
*/
|
|
public final class ScanResult implements Parcelable, Attributable {
|
|
|
|
/**
|
|
* 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;
|
|
}
|
|
|
|
/** {@hide} */
|
|
public void setAttributionSource(@NonNull AttributionSource attributionSource) {
|
|
Attributable.setAttributionSource(mDevice, attributionSource);
|
|
}
|
|
|
|
/**
|
|
* 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(@Nullable 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];
|
|
}
|
|
};
|
|
|
|
}
|