Home | History | Annotate | Download | only in le
      1 /*
      2  * Copyright (C) 2014 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package android.bluetooth.le;
     18 
     19 import android.annotation.Nullable;
     20 import android.bluetooth.BluetoothDevice;
     21 import android.os.Parcel;
     22 import android.os.Parcelable;
     23 
     24 import java.util.Objects;
     25 
     26 /**
     27  * ScanResult for Bluetooth LE scan.
     28  */
     29 public final class ScanResult implements Parcelable {
     30 
     31     /**
     32      * For chained advertisements, inidcates tha the data contained in this
     33      * scan result is complete.
     34      */
     35     public static final int DATA_COMPLETE = 0x00;
     36 
     37     /**
     38      * For chained advertisements, indicates that the controller was
     39      * unable to receive all chained packets and the scan result contains
     40      * incomplete truncated data.
     41      */
     42     public static final int DATA_TRUNCATED = 0x02;
     43 
     44     /**
     45      * Indicates that the secondary physical layer was not used.
     46      */
     47     public static final int PHY_UNUSED = 0x00;
     48 
     49     /**
     50      * Advertising Set ID is not present in the packet.
     51      */
     52     public static final int SID_NOT_PRESENT = 0xFF;
     53 
     54     /**
     55      * TX power is not present in the packet.
     56      */
     57     public static final int TX_POWER_NOT_PRESENT = 0x7F;
     58 
     59     /**
     60      * Periodic advertising interval is not present in the packet.
     61      */
     62     public static final int PERIODIC_INTERVAL_NOT_PRESENT = 0x00;
     63 
     64     /**
     65      * Mask for checking whether event type represents legacy advertisement.
     66      */
     67     private static final int ET_LEGACY_MASK = 0x10;
     68 
     69     /**
     70      * Mask for checking whether event type represents connectable advertisement.
     71      */
     72     private static final int ET_CONNECTABLE_MASK = 0x01;
     73 
     74     // Remote Bluetooth device.
     75     private BluetoothDevice mDevice;
     76 
     77     // Scan record, including advertising data and scan response data.
     78     @Nullable
     79     private ScanRecord mScanRecord;
     80 
     81     // Received signal strength.
     82     private int mRssi;
     83 
     84     // Device timestamp when the result was last seen.
     85     private long mTimestampNanos;
     86 
     87     private int mEventType;
     88     private int mPrimaryPhy;
     89     private int mSecondaryPhy;
     90     private int mAdvertisingSid;
     91     private int mTxPower;
     92     private int mPeriodicAdvertisingInterval;
     93 
     94     /**
     95      * Constructs a new ScanResult.
     96      *
     97      * @param device Remote Bluetooth device found.
     98      * @param scanRecord Scan record including both advertising data and scan response data.
     99      * @param rssi Received signal strength.
    100      * @param timestampNanos Timestamp at which the scan result was observed.
    101      * @deprecated use {@link #ScanResult(BluetoothDevice, int, int, int, int, int, int, int,
    102      * ScanRecord, long)}
    103      */
    104     @Deprecated
    105     public ScanResult(BluetoothDevice device, ScanRecord scanRecord, int rssi,
    106             long timestampNanos) {
    107         mDevice = device;
    108         mScanRecord = scanRecord;
    109         mRssi = rssi;
    110         mTimestampNanos = timestampNanos;
    111         mEventType = (DATA_COMPLETE << 5) | ET_LEGACY_MASK | ET_CONNECTABLE_MASK;
    112         mPrimaryPhy = BluetoothDevice.PHY_LE_1M;
    113         mSecondaryPhy = PHY_UNUSED;
    114         mAdvertisingSid = SID_NOT_PRESENT;
    115         mTxPower = 127;
    116         mPeriodicAdvertisingInterval = 0;
    117     }
    118 
    119     /**
    120      * Constructs a new ScanResult.
    121      *
    122      * @param device Remote Bluetooth device found.
    123      * @param eventType Event type.
    124      * @param primaryPhy Primary advertising phy.
    125      * @param secondaryPhy Secondary advertising phy.
    126      * @param advertisingSid Advertising set ID.
    127      * @param txPower Transmit power.
    128      * @param rssi Received signal strength.
    129      * @param periodicAdvertisingInterval Periodic advertising interval.
    130      * @param scanRecord Scan record including both advertising data and scan response data.
    131      * @param timestampNanos Timestamp at which the scan result was observed.
    132      */
    133     public ScanResult(BluetoothDevice device, int eventType, int primaryPhy, int secondaryPhy,
    134             int advertisingSid, int txPower, int rssi, int periodicAdvertisingInterval,
    135             ScanRecord scanRecord, long timestampNanos) {
    136         mDevice = device;
    137         mEventType = eventType;
    138         mPrimaryPhy = primaryPhy;
    139         mSecondaryPhy = secondaryPhy;
    140         mAdvertisingSid = advertisingSid;
    141         mTxPower = txPower;
    142         mRssi = rssi;
    143         mPeriodicAdvertisingInterval = periodicAdvertisingInterval;
    144         mScanRecord = scanRecord;
    145         mTimestampNanos = timestampNanos;
    146     }
    147 
    148     private ScanResult(Parcel in) {
    149         readFromParcel(in);
    150     }
    151 
    152     @Override
    153     public void writeToParcel(Parcel dest, int flags) {
    154         if (mDevice != null) {
    155             dest.writeInt(1);
    156             mDevice.writeToParcel(dest, flags);
    157         } else {
    158             dest.writeInt(0);
    159         }
    160         if (mScanRecord != null) {
    161             dest.writeInt(1);
    162             dest.writeByteArray(mScanRecord.getBytes());
    163         } else {
    164             dest.writeInt(0);
    165         }
    166         dest.writeInt(mRssi);
    167         dest.writeLong(mTimestampNanos);
    168         dest.writeInt(mEventType);
    169         dest.writeInt(mPrimaryPhy);
    170         dest.writeInt(mSecondaryPhy);
    171         dest.writeInt(mAdvertisingSid);
    172         dest.writeInt(mTxPower);
    173         dest.writeInt(mPeriodicAdvertisingInterval);
    174     }
    175 
    176     private void readFromParcel(Parcel in) {
    177         if (in.readInt() == 1) {
    178             mDevice = BluetoothDevice.CREATOR.createFromParcel(in);
    179         }
    180         if (in.readInt() == 1) {
    181             mScanRecord = ScanRecord.parseFromBytes(in.createByteArray());
    182         }
    183         mRssi = in.readInt();
    184         mTimestampNanos = in.readLong();
    185         mEventType = in.readInt();
    186         mPrimaryPhy = in.readInt();
    187         mSecondaryPhy = in.readInt();
    188         mAdvertisingSid = in.readInt();
    189         mTxPower = in.readInt();
    190         mPeriodicAdvertisingInterval = in.readInt();
    191     }
    192 
    193     @Override
    194     public int describeContents() {
    195         return 0;
    196     }
    197 
    198     /**
    199      * Returns the remote Bluetooth device identified by the Bluetooth device address.
    200      */
    201     public BluetoothDevice getDevice() {
    202         return mDevice;
    203     }
    204 
    205     /**
    206      * Returns the scan record, which is a combination of advertisement and scan response.
    207      */
    208     @Nullable
    209     public ScanRecord getScanRecord() {
    210         return mScanRecord;
    211     }
    212 
    213     /**
    214      * Returns the received signal strength in dBm. The valid range is [-127, 126].
    215      */
    216     public int getRssi() {
    217         return mRssi;
    218     }
    219 
    220     /**
    221      * Returns timestamp since boot when the scan record was observed.
    222      */
    223     public long getTimestampNanos() {
    224         return mTimestampNanos;
    225     }
    226 
    227     /**
    228      * Returns true if this object represents legacy scan result.
    229      * Legacy scan results do not contain advanced advertising information
    230      * as specified in the Bluetooth Core Specification v5.
    231      */
    232     public boolean isLegacy() {
    233         return (mEventType & ET_LEGACY_MASK) != 0;
    234     }
    235 
    236     /**
    237      * Returns true if this object represents connectable scan result.
    238      */
    239     public boolean isConnectable() {
    240         return (mEventType & ET_CONNECTABLE_MASK) != 0;
    241     }
    242 
    243     /**
    244      * Returns the data status.
    245      * Can be one of {@link ScanResult#DATA_COMPLETE} or
    246      * {@link ScanResult#DATA_TRUNCATED}.
    247      */
    248     public int getDataStatus() {
    249         // return bit 5 and 6
    250         return (mEventType >> 5) & 0x03;
    251     }
    252 
    253     /**
    254      * Returns the primary Physical Layer
    255      * on which this advertisment was received.
    256      * Can be one of {@link BluetoothDevice#PHY_LE_1M} or
    257      * {@link BluetoothDevice#PHY_LE_CODED}.
    258      */
    259     public int getPrimaryPhy() {
    260         return mPrimaryPhy;
    261     }
    262 
    263     /**
    264      * Returns the secondary Physical Layer
    265      * on which this advertisment was received.
    266      * Can be one of {@link BluetoothDevice#PHY_LE_1M},
    267      * {@link BluetoothDevice#PHY_LE_2M}, {@link BluetoothDevice#PHY_LE_CODED}
    268      * or {@link ScanResult#PHY_UNUSED} - if the advertisement
    269      * was not received on a secondary physical channel.
    270      */
    271     public int getSecondaryPhy() {
    272         return mSecondaryPhy;
    273     }
    274 
    275     /**
    276      * Returns the advertising set id.
    277      * May return {@link ScanResult#SID_NOT_PRESENT} if
    278      * no set id was is present.
    279      */
    280     public int getAdvertisingSid() {
    281         return mAdvertisingSid;
    282     }
    283 
    284     /**
    285      * Returns the transmit power in dBm.
    286      * Valid range is [-127, 126]. A value of {@link ScanResult#TX_POWER_NOT_PRESENT}
    287      * indicates that the TX power is not present.
    288      */
    289     public int getTxPower() {
    290         return mTxPower;
    291     }
    292 
    293     /**
    294      * Returns the periodic advertising interval in units of 1.25ms.
    295      * Valid range is 6 (7.5ms) to 65536 (81918.75ms). A value of
    296      * {@link ScanResult#PERIODIC_INTERVAL_NOT_PRESENT} means periodic
    297      * advertising interval is not present.
    298      */
    299     public int getPeriodicAdvertisingInterval() {
    300         return mPeriodicAdvertisingInterval;
    301     }
    302 
    303     @Override
    304     public int hashCode() {
    305         return Objects.hash(mDevice, mRssi, mScanRecord, mTimestampNanos,
    306                 mEventType, mPrimaryPhy, mSecondaryPhy,
    307                 mAdvertisingSid, mTxPower,
    308                 mPeriodicAdvertisingInterval);
    309     }
    310 
    311     @Override
    312     public boolean equals(Object obj) {
    313         if (this == obj) {
    314             return true;
    315         }
    316         if (obj == null || getClass() != obj.getClass()) {
    317             return false;
    318         }
    319         ScanResult other = (ScanResult) obj;
    320         return Objects.equals(mDevice, other.mDevice) && (mRssi == other.mRssi)
    321                 && Objects.equals(mScanRecord, other.mScanRecord)
    322                 && (mTimestampNanos == other.mTimestampNanos)
    323                 && mEventType == other.mEventType
    324                 && mPrimaryPhy == other.mPrimaryPhy
    325                 && mSecondaryPhy == other.mSecondaryPhy
    326                 && mAdvertisingSid == other.mAdvertisingSid
    327                 && mTxPower == other.mTxPower
    328                 && mPeriodicAdvertisingInterval == other.mPeriodicAdvertisingInterval;
    329     }
    330 
    331     @Override
    332     public String toString() {
    333         return "ScanResult{" + "device=" + mDevice + ", scanRecord="
    334                 + Objects.toString(mScanRecord) + ", rssi=" + mRssi
    335                 + ", timestampNanos=" + mTimestampNanos + ", eventType=" + mEventType
    336                 + ", primaryPhy=" + mPrimaryPhy + ", secondaryPhy=" + mSecondaryPhy
    337                 + ", advertisingSid=" + mAdvertisingSid + ", txPower=" + mTxPower
    338                 + ", periodicAdvertisingInterval=" + mPeriodicAdvertisingInterval + '}';
    339     }
    340 
    341     public static final Parcelable.Creator<ScanResult> CREATOR = new Creator<ScanResult>() {
    342         @Override
    343         public ScanResult createFromParcel(Parcel source) {
    344             return new ScanResult(source);
    345         }
    346 
    347         @Override
    348         public ScanResult[] newArray(int size) {
    349             return new ScanResult[size];
    350         }
    351     };
    352 
    353 }
    354