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
17package android.bluetooth.le;
18
19import android.annotation.SystemApi;
20import android.bluetooth.BluetoothDevice;
21import android.os.Parcel;
22import android.os.Parcelable;
23
24/**
25 * Bluetooth LE scan settings are passed to {@link BluetoothLeScanner#startScan} to define the
26 * parameters for the scan.
27 */
28public final class ScanSettings implements Parcelable {
29
30    /**
31     * A special Bluetooth LE scan mode. Applications using this scan mode will passively listen for
32     * other scan results without starting BLE scans themselves.
33     */
34    public static final int SCAN_MODE_OPPORTUNISTIC = -1;
35
36    /**
37     * Perform Bluetooth LE scan in low power mode. This is the default scan mode as it consumes the
38     * least power.
39     */
40    public static final int SCAN_MODE_LOW_POWER = 0;
41
42    /**
43     * Perform Bluetooth LE scan in balanced power mode. Scan results are returned at a rate that
44     * provides a good trade-off between scan frequency and power consumption.
45     */
46    public static final int SCAN_MODE_BALANCED = 1;
47
48    /**
49     * Scan using highest duty cycle. It's recommended to only use this mode when the application is
50     * running in the foreground.
51     */
52    public static final int SCAN_MODE_LOW_LATENCY = 2;
53
54    /**
55     * Trigger a callback for every Bluetooth advertisement found that matches the filter criteria.
56     * If no filter is active, all advertisement packets are reported.
57     */
58    public static final int CALLBACK_TYPE_ALL_MATCHES = 1;
59
60    /**
61     * A result callback is only triggered for the first advertisement packet received that matches
62     * the filter criteria.
63     */
64    public static final int CALLBACK_TYPE_FIRST_MATCH = 2;
65
66    /**
67     * Receive a callback when advertisements are no longer received from a device that has been
68     * previously reported by a first match callback.
69     */
70    public static final int CALLBACK_TYPE_MATCH_LOST = 4;
71
72
73    /**
74     * Determines how many advertisements to match per filter, as this is scarce hw resource
75     */
76    /**
77     * Match one advertisement per filter
78     */
79    public static final int MATCH_NUM_ONE_ADVERTISEMENT = 1;
80
81    /**
82     * Match few advertisement per filter, depends on current capability and availibility of
83     * the resources in hw
84     */
85    public static final int MATCH_NUM_FEW_ADVERTISEMENT = 2;
86
87    /**
88     * Match as many advertisement per filter as hw could allow, depends on current
89     * capability and availibility of the resources in hw
90     */
91    public static final int MATCH_NUM_MAX_ADVERTISEMENT = 3;
92
93
94    /**
95     * In Aggressive mode, hw will determine a match sooner even with feeble signal strength
96     * and few number of sightings/match in a duration.
97     */
98    public static final int MATCH_MODE_AGGRESSIVE = 1;
99
100    /**
101     * For sticky mode, higher threshold of signal strength and sightings is required
102     * before reporting by hw
103     */
104    public static final int MATCH_MODE_STICKY = 2;
105
106    /**
107     * Request full scan results which contain the device, rssi, advertising data, scan response
108     * as well as the scan timestamp.
109     *
110     * @hide
111     */
112    @SystemApi
113    public static final int SCAN_RESULT_TYPE_FULL = 0;
114
115    /**
116     * Request abbreviated scan results which contain the device, rssi and scan timestamp.
117     * <p>
118     * <b>Note:</b> It is possible for an application to get more scan results than it asked for, if
119     * there are multiple apps using this type.
120     *
121     * @hide
122     */
123    @SystemApi
124    public static final int SCAN_RESULT_TYPE_ABBREVIATED = 1;
125
126    /**
127     * Use all supported PHYs for scanning.
128     * This will check the controller capabilities, and start
129     * the scan on 1Mbit and LE Coded PHYs if supported, or on
130     * the 1Mbit PHY only.
131     */
132    public static final int PHY_LE_ALL_SUPPORTED = 255;
133
134    // Bluetooth LE scan mode.
135    private int mScanMode;
136
137    // Bluetooth LE scan callback type
138    private int mCallbackType;
139
140    // Bluetooth LE scan result type
141    private int mScanResultType;
142
143    // Time of delay for reporting the scan result
144    private long mReportDelayMillis;
145
146    private int mMatchMode;
147
148    private int mNumOfMatchesPerFilter;
149
150    // Include only legacy advertising results
151    private boolean mLegacy;
152
153    private int mPhy;
154
155    public int getScanMode() {
156        return mScanMode;
157    }
158
159    public int getCallbackType() {
160        return mCallbackType;
161    }
162
163    public int getScanResultType() {
164        return mScanResultType;
165    }
166
167    /**
168     * @hide
169     */
170    public int getMatchMode() {
171        return mMatchMode;
172    }
173
174    /**
175     * @hide
176     */
177    public int getNumOfMatches() {
178        return mNumOfMatchesPerFilter;
179    }
180
181    /**
182     * Returns whether only legacy advertisements will be returned.
183     * Legacy advertisements include advertisements as specified
184     * by the Bluetooth core specification 4.2 and below.
185     */
186    public boolean getLegacy() {
187        return mLegacy;
188    }
189
190    /**
191     * Returns the physical layer used during a scan.
192     */
193    public int getPhy() {
194        return mPhy;
195    }
196
197    /**
198     * Returns report delay timestamp based on the device clock.
199     */
200    public long getReportDelayMillis() {
201        return mReportDelayMillis;
202    }
203
204    private ScanSettings(int scanMode, int callbackType, int scanResultType,
205                         long reportDelayMillis, int matchMode,
206                         int numOfMatchesPerFilter, boolean legacy, int phy) {
207        mScanMode = scanMode;
208        mCallbackType = callbackType;
209        mScanResultType = scanResultType;
210        mReportDelayMillis = reportDelayMillis;
211        mNumOfMatchesPerFilter = numOfMatchesPerFilter;
212        mMatchMode = matchMode;
213        mLegacy = legacy;
214        mPhy = phy;
215    }
216
217    private ScanSettings(Parcel in) {
218        mScanMode = in.readInt();
219        mCallbackType = in.readInt();
220        mScanResultType = in.readInt();
221        mReportDelayMillis = in.readLong();
222        mMatchMode = in.readInt();
223        mNumOfMatchesPerFilter = in.readInt();
224        mLegacy = in.readInt() != 0 ? true : false;
225        mPhy = in.readInt();
226    }
227
228    @Override
229    public void writeToParcel(Parcel dest, int flags) {
230        dest.writeInt(mScanMode);
231        dest.writeInt(mCallbackType);
232        dest.writeInt(mScanResultType);
233        dest.writeLong(mReportDelayMillis);
234        dest.writeInt(mMatchMode);
235        dest.writeInt(mNumOfMatchesPerFilter);
236        dest.writeInt(mLegacy ? 1 : 0);
237        dest.writeInt(mPhy);
238    }
239
240    @Override
241    public int describeContents() {
242        return 0;
243    }
244
245    public static final Parcelable.Creator<ScanSettings>
246            CREATOR = new Creator<ScanSettings>() {
247                    @Override
248                public ScanSettings[] newArray(int size) {
249                    return new ScanSettings[size];
250                }
251
252                    @Override
253                public ScanSettings createFromParcel(Parcel in) {
254                    return new ScanSettings(in);
255                }
256            };
257
258    /**
259     * Builder for {@link ScanSettings}.
260     */
261    public static final class Builder {
262        private int mScanMode = SCAN_MODE_LOW_POWER;
263        private int mCallbackType = CALLBACK_TYPE_ALL_MATCHES;
264        private int mScanResultType = SCAN_RESULT_TYPE_FULL;
265        private long mReportDelayMillis = 0;
266        private int mMatchMode = MATCH_MODE_AGGRESSIVE;
267        private int mNumOfMatchesPerFilter  = MATCH_NUM_MAX_ADVERTISEMENT;
268        private boolean mLegacy = true;
269        private int mPhy = PHY_LE_ALL_SUPPORTED;
270
271        /**
272         * Set scan mode for Bluetooth LE scan.
273         *
274         * @param scanMode The scan mode can be one of {@link ScanSettings#SCAN_MODE_LOW_POWER},
275         *            {@link ScanSettings#SCAN_MODE_BALANCED} or
276         *            {@link ScanSettings#SCAN_MODE_LOW_LATENCY}.
277         * @throws IllegalArgumentException If the {@code scanMode} is invalid.
278         */
279        public Builder setScanMode(int scanMode) {
280            if (scanMode < SCAN_MODE_OPPORTUNISTIC || scanMode > SCAN_MODE_LOW_LATENCY) {
281                throw new IllegalArgumentException("invalid scan mode " + scanMode);
282            }
283            mScanMode = scanMode;
284            return this;
285        }
286
287        /**
288         * Set callback type for Bluetooth LE scan.
289         *
290         * @param callbackType The callback type flags for the scan.
291         * @throws IllegalArgumentException If the {@code callbackType} is invalid.
292         */
293        public Builder setCallbackType(int callbackType) {
294
295            if (!isValidCallbackType(callbackType)) {
296                throw new IllegalArgumentException("invalid callback type - " + callbackType);
297            }
298            mCallbackType = callbackType;
299            return this;
300        }
301
302        // Returns true if the callbackType is valid.
303        private boolean isValidCallbackType(int callbackType) {
304            if (callbackType == CALLBACK_TYPE_ALL_MATCHES ||
305                    callbackType == CALLBACK_TYPE_FIRST_MATCH ||
306                    callbackType == CALLBACK_TYPE_MATCH_LOST) {
307                return true;
308            }
309            return callbackType == (CALLBACK_TYPE_FIRST_MATCH | CALLBACK_TYPE_MATCH_LOST);
310        }
311
312        /**
313         * Set scan result type for Bluetooth LE scan.
314         *
315         * @param scanResultType Type for scan result, could be either
316         *            {@link ScanSettings#SCAN_RESULT_TYPE_FULL} or
317         *            {@link ScanSettings#SCAN_RESULT_TYPE_ABBREVIATED}.
318         * @throws IllegalArgumentException If the {@code scanResultType} is invalid.
319         * @hide
320         */
321        @SystemApi
322        public Builder setScanResultType(int scanResultType) {
323            if (scanResultType < SCAN_RESULT_TYPE_FULL
324                    || scanResultType > SCAN_RESULT_TYPE_ABBREVIATED) {
325                throw new IllegalArgumentException(
326                        "invalid scanResultType - " + scanResultType);
327            }
328            mScanResultType = scanResultType;
329            return this;
330        }
331
332        /**
333         * Set report delay timestamp for Bluetooth LE scan.
334         *
335         * @param reportDelayMillis Delay of report in milliseconds. Set to 0 to be notified of
336         *            results immediately. Values &gt; 0 causes the scan results to be queued up and
337         *            delivered after the requested delay or when the internal buffers fill up.
338         * @throws IllegalArgumentException If {@code reportDelayMillis} &lt; 0.
339         */
340        public Builder setReportDelay(long reportDelayMillis) {
341            if (reportDelayMillis < 0) {
342                throw new IllegalArgumentException("reportDelay must be > 0");
343            }
344            mReportDelayMillis = reportDelayMillis;
345            return this;
346        }
347
348        /**
349         * Set the number of matches for Bluetooth LE scan filters hardware match
350         *
351         * @param numOfMatches The num of matches can be one of
352         *              {@link ScanSettings#MATCH_NUM_ONE_ADVERTISEMENT} or
353         *              {@link ScanSettings#MATCH_NUM_FEW_ADVERTISEMENT} or
354         *              {@link ScanSettings#MATCH_NUM_MAX_ADVERTISEMENT}
355         * @throws IllegalArgumentException If the {@code matchMode} is invalid.
356         */
357        public Builder setNumOfMatches(int numOfMatches) {
358            if (numOfMatches < MATCH_NUM_ONE_ADVERTISEMENT
359                    || numOfMatches > MATCH_NUM_MAX_ADVERTISEMENT) {
360                throw new IllegalArgumentException("invalid numOfMatches " + numOfMatches);
361            }
362            mNumOfMatchesPerFilter = numOfMatches;
363            return this;
364        }
365
366        /**
367         * Set match mode for Bluetooth LE scan filters hardware match
368         *
369         * @param matchMode The match mode can be one of
370         *              {@link ScanSettings#MATCH_MODE_AGGRESSIVE} or
371         *              {@link ScanSettings#MATCH_MODE_STICKY}
372         * @throws IllegalArgumentException If the {@code matchMode} is invalid.
373         */
374        public Builder setMatchMode(int matchMode) {
375            if (matchMode < MATCH_MODE_AGGRESSIVE
376                    || matchMode > MATCH_MODE_STICKY) {
377                throw new IllegalArgumentException("invalid matchMode " + matchMode);
378            }
379            mMatchMode = matchMode;
380            return this;
381        }
382
383        /**
384         * Set whether only legacy advertisments should be returned in scan results.
385         * Legacy advertisements include advertisements as specified by the
386         * Bluetooth core specification 4.2 and below. This is true by default
387         * for compatibility with older apps.
388         *
389         * @param legacy true if only legacy advertisements will be returned
390         */
391        public Builder setLegacy(boolean legacy) {
392            mLegacy = legacy;
393            return this;
394        }
395
396        /**
397         * Set the Physical Layer to use during this scan.
398         * This is used only if {@link ScanSettings.Builder#setLegacy}
399         * is set to false.
400         * {@link android.bluetooth.BluetoothAdapter#isLeCodedPhySupported}
401         * may be used to check whether LE Coded phy is supported by calling
402         * {@link android.bluetooth.BluetoothAdapter#isLeCodedPhySupported}.
403         * Selecting an unsupported phy will result in failure to start scan.
404         *
405         * @param phy Can be one of
406         *   {@link BluetoothDevice#PHY_LE_1M},
407         *   {@link BluetoothDevice#PHY_LE_CODED} or
408         *   {@link ScanSettings#PHY_LE_ALL_SUPPORTED}
409         */
410        public Builder setPhy(int phy) {
411            mPhy = phy;
412            return this;
413        }
414
415        /**
416         * Build {@link ScanSettings}.
417         */
418        public ScanSettings build() {
419            return new ScanSettings(mScanMode, mCallbackType, mScanResultType,
420                                    mReportDelayMillis, mMatchMode,
421                                    mNumOfMatchesPerFilter, mLegacy, mPhy);
422        }
423    }
424}
425