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