• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2021 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 com.android.server.nearby.common.ble;
18 
19 import android.bluetooth.BluetoothDevice;
20 import android.bluetooth.le.ScanRecord;
21 import android.bluetooth.le.ScanResult;
22 import android.os.Build.VERSION_CODES;
23 import android.os.Parcel;
24 import android.os.Parcelable;
25 
26 import androidx.annotation.Nullable;
27 import androidx.annotation.RequiresApi;
28 import androidx.annotation.VisibleForTesting;
29 
30 import java.util.Arrays;
31 import java.util.Objects;
32 import java.util.concurrent.TimeUnit;
33 
34 /**
35  * A sighting of a BLE device found in a Bluetooth LE scan.
36  */
37 
38 public class BleSighting implements Parcelable {
39 
40     public static final Parcelable.Creator<BleSighting> CREATOR = new Creator<BleSighting>() {
41         @Override
42         public BleSighting createFromParcel(Parcel source) {
43             BleSighting nBleSighting = new BleSighting(source.readParcelable(null),
44                     source.marshall(), source.readInt(), source.readLong());
45             return null;
46         }
47 
48         @Override
49         public BleSighting[] newArray(int size) {
50             return new BleSighting[size];
51         }
52     };
53 
54     // Max and min rssi value which is from {@link android.bluetooth.le.ScanResult#getRssi()}.
55     @VisibleForTesting
56     public static final int MAX_RSSI_VALUE = 126;
57     @VisibleForTesting
58     public static final int MIN_RSSI_VALUE = -127;
59 
60     /** Remote bluetooth device. */
61     private final BluetoothDevice mDevice;
62 
63     /**
64      * BLE record, including advertising data and response data. BleRecord is not parcelable, so
65      * this
66      * is created from bleRecordBytes.
67      */
68     private final BleRecord mBleRecord;
69 
70     /** The bytes of a BLE record. */
71     private final byte[] mBleRecordBytes;
72 
73     /** Received signal strength. */
74     private final int mRssi;
75 
76     /** Nanos timestamp when the ble device was observed (epoch time). */
77     private final long mTimestampEpochNanos;
78 
79     /**
80      * Constructor of a BLE sighting.
81      *
82      * @param device              Remote bluetooth device that is found.
83      * @param bleRecordBytes      The bytes that will create a BleRecord.
84      * @param rssi                Received signal strength.
85      * @param timestampEpochNanos Nanos timestamp when the BLE device was observed (epoch time).
86      */
BleSighting(BluetoothDevice device, byte[] bleRecordBytes, int rssi, long timestampEpochNanos)87     public BleSighting(BluetoothDevice device, byte[] bleRecordBytes, int rssi,
88             long timestampEpochNanos) {
89         this.mDevice = device;
90         this.mBleRecordBytes = bleRecordBytes;
91         this.mRssi = rssi;
92         this.mTimestampEpochNanos = timestampEpochNanos;
93         mBleRecord = BleRecord.parseFromBytes(bleRecordBytes);
94     }
95 
96     @Override
describeContents()97     public int describeContents() {
98         return 0;
99     }
100 
101     /** Returns the remote bluetooth device identified by the bluetooth device address. */
getDevice()102     public BluetoothDevice getDevice() {
103         return mDevice;
104     }
105 
106     /** Returns the BLE record, which is a combination of advertisement and scan response. */
getBleRecord()107     public BleRecord getBleRecord() {
108         return mBleRecord;
109     }
110 
111     /** Returns the bytes of the BLE record. */
getBleRecordBytes()112     public byte[] getBleRecordBytes() {
113         return mBleRecordBytes;
114     }
115 
116     /** Returns the received signal strength in dBm. The valid range is [-127, 127]. */
getRssi()117     public int getRssi() {
118         return mRssi;
119     }
120 
121     /**
122      * Returns the received signal strength normalized with the offset specific to the given device.
123      * 3 is the rssi offset to calculate fast init distance.
124      * <p>This method utilized the rssi offset maintained by Nearby Sharing.
125      *
126      * @return normalized rssi which is between [-127, 126] according to {@link
127      * android.bluetooth.le.ScanResult#getRssi()}.
128      */
getNormalizedRSSI()129     public int getNormalizedRSSI() {
130         int adjustedRssi = mRssi + 3;
131         if (adjustedRssi < MIN_RSSI_VALUE) {
132             return MIN_RSSI_VALUE;
133         } else if (adjustedRssi > MAX_RSSI_VALUE) {
134             return MAX_RSSI_VALUE;
135         } else {
136             return adjustedRssi;
137         }
138     }
139 
140     /** Returns timestamp in epoch time when the scan record was observed. */
getTimestampNanos()141     public long getTimestampNanos() {
142         return mTimestampEpochNanos;
143     }
144 
145     /** Returns timestamp in epoch time when the scan record was observed, in millis. */
getTimestampMillis()146     public long getTimestampMillis() {
147         return TimeUnit.NANOSECONDS.toMillis(mTimestampEpochNanos);
148     }
149 
150     @Override
writeToParcel(Parcel dest, int flags)151     public void writeToParcel(Parcel dest, int flags) {
152         dest.writeParcelable(mDevice, flags);
153         dest.writeByteArray(mBleRecordBytes);
154         dest.writeInt(mRssi);
155         dest.writeLong(mTimestampEpochNanos);
156     }
157 
158     @Override
hashCode()159     public int hashCode() {
160         return Objects.hash(mDevice, mRssi, mTimestampEpochNanos, Arrays.hashCode(mBleRecordBytes));
161     }
162 
163     @Override
equals(@ullable Object obj)164     public boolean equals(@Nullable Object obj) {
165         if (this == obj) {
166             return true;
167         }
168         if (!(obj instanceof BleSighting)) {
169             return false;
170         }
171         BleSighting other = (BleSighting) obj;
172         return Objects.equals(mDevice, other.mDevice)
173                 && mRssi == other.mRssi
174                 && Arrays.equals(mBleRecordBytes, other.mBleRecordBytes)
175                 && mTimestampEpochNanos == other.mTimestampEpochNanos;
176     }
177 
178     @Override
toString()179     public String toString() {
180         return "BleSighting{"
181                 + "device="
182                 + mDevice
183                 + ", bleRecord="
184                 + mBleRecord
185                 + ", rssi="
186                 + mRssi
187                 + ", timestampNanos="
188                 + mTimestampEpochNanos
189                 + "}";
190     }
191 
192     /** Creates {@link BleSighting} using the {@link ScanResult}. */
193     @RequiresApi(api = VERSION_CODES.LOLLIPOP)
194     @Nullable
createFromOsScanResult(ScanResult osResult)195     public static BleSighting createFromOsScanResult(ScanResult osResult) {
196         ScanRecord osScanRecord = osResult.getScanRecord();
197         if (osScanRecord == null) {
198             return null;
199         }
200 
201         return new BleSighting(
202                 osResult.getDevice(),
203                 osScanRecord.getBytes(),
204                 osResult.getRssi(),
205                 // The timestamp from ScanResult is 'nanos since boot', Beacon lib will change it
206                 // as 'nanos
207                 // since epoch', but Nearby never reference this field, just pass it as 'nanos
208                 // since boot'.
209                 // ref to beacon/scan/impl/LBluetoothLeScannerCompat.fromOs for beacon design
210                 // about how to
211                 // convert nanos since boot to epoch.
212                 osResult.getTimestampNanos());
213     }
214 }
215 
216