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 android.nearby;
18 
19 import android.Manifest;
20 import android.annotation.IntDef;
21 import android.annotation.NonNull;
22 import android.annotation.Nullable;
23 import android.annotation.RequiresPermission;
24 import android.annotation.SystemApi;
25 import android.os.Build;
26 import android.os.Parcel;
27 import android.os.Parcelable;
28 import android.os.WorkSource;
29 
30 import com.android.internal.util.Preconditions;
31 
32 import java.lang.annotation.Retention;
33 import java.lang.annotation.RetentionPolicy;
34 import java.util.ArrayList;
35 import java.util.List;
36 import java.util.Objects;
37 import java.util.concurrent.Executor;
38 import java.util.function.Consumer;
39 
40 /**
41  * An encapsulation of various parameters for requesting nearby scans.
42  *
43  * @hide
44  */
45 @SystemApi
46 public final class ScanRequest implements Parcelable {
47 
48     /** Scan type for scanning devices using fast pair protocol. */
49     public static final int SCAN_TYPE_FAST_PAIR = 1;
50     /** Scan type for scanning devices using nearby presence protocol. */
51     public static final int SCAN_TYPE_NEARBY_PRESENCE = 2;
52 
53     /** Scan mode uses highest duty cycle. */
54     public static final int SCAN_MODE_LOW_LATENCY = 2;
55     /** Scan in balanced power mode.
56      *  Scan results are returned at a rate that provides a good trade-off between scan
57      *  frequency and power consumption.
58      */
59     public static final int SCAN_MODE_BALANCED = 1;
60     /** Perform scan in low power mode. This is the default scan mode. */
61     public static final int SCAN_MODE_LOW_POWER = 0;
62     /**
63      * A special scan mode. Applications using this scan mode will passively listen for other scan
64      * results without starting BLE scans themselves.
65      */
66     public static final int SCAN_MODE_NO_POWER = -1;
67     /**
68      * A special scan mode to indicate that client only wants to use CHRE to scan.
69      *
70      * @hide
71      */
72     public static final int SCAN_MODE_CHRE_ONLY = 3;
73     /**
74      * Used to read a ScanRequest from a Parcel.
75      */
76     @NonNull
77     public static final Creator<ScanRequest> CREATOR = new Creator<ScanRequest>() {
78         @Override
79         public ScanRequest createFromParcel(Parcel in) {
80             ScanRequest.Builder builder = new ScanRequest.Builder()
81                     .setScanType(in.readInt())
82                     .setScanMode(in.readInt())
83                     .setBleEnabled(in.readBoolean())
84                     .setOffloadOnly(in.readBoolean())
85                     .setWorkSource(in.readTypedObject(WorkSource.CREATOR));
86             final int size = in.readInt();
87             for (int i = 0; i < size; i++) {
88                 builder.addScanFilter(ScanFilter.createFromParcel(in));
89             }
90             return builder.build();
91         }
92 
93         @Override
94         public ScanRequest[] newArray(int size) {
95             return new ScanRequest[size];
96         }
97     };
98 
99     private final @ScanType int mScanType;
100     private final @ScanMode int mScanMode;
101     private final boolean mBleEnabled;
102     private final boolean mOffloadOnly;
103     private final @NonNull WorkSource mWorkSource;
104     private final List<ScanFilter> mScanFilters;
105 
ScanRequest(@canType int scanType, @ScanMode int scanMode, boolean bleEnabled, boolean offloadOnly, @NonNull WorkSource workSource, List<ScanFilter> scanFilters)106     private ScanRequest(@ScanType int scanType, @ScanMode int scanMode, boolean bleEnabled,
107             boolean offloadOnly, @NonNull WorkSource workSource, List<ScanFilter> scanFilters) {
108         mScanType = scanType;
109         mScanMode = scanMode;
110         mBleEnabled = bleEnabled;
111         mOffloadOnly = offloadOnly;
112         mWorkSource = workSource;
113         mScanFilters = scanFilters;
114     }
115 
116     /**
117      * Convert scan mode to readable string.
118      *
119      * @param scanMode Integer that may represent a{@link ScanMode}.
120      */
121     @NonNull
scanModeToString(@canMode int scanMode)122     public static String scanModeToString(@ScanMode int scanMode) {
123         switch (scanMode) {
124             case SCAN_MODE_LOW_LATENCY:
125                 return "SCAN_MODE_LOW_LATENCY";
126             case SCAN_MODE_BALANCED:
127                 return "SCAN_MODE_BALANCED";
128             case SCAN_MODE_LOW_POWER:
129                 return "SCAN_MODE_LOW_POWER";
130             case SCAN_MODE_NO_POWER:
131                 return "SCAN_MODE_NO_POWER";
132             default:
133                 return "SCAN_MODE_INVALID";
134         }
135     }
136 
137     /**
138      * Returns true if an integer is a defined scan type.
139      */
isValidScanType(@canType int scanType)140     public static boolean isValidScanType(@ScanType int scanType) {
141         return scanType == SCAN_TYPE_FAST_PAIR
142                 || scanType == SCAN_TYPE_NEARBY_PRESENCE;
143     }
144 
145     /**
146      * Returns true if an integer is a defined scan mode.
147      */
isValidScanMode(@canMode int scanMode)148     public static boolean isValidScanMode(@ScanMode int scanMode) {
149         return scanMode == SCAN_MODE_LOW_LATENCY
150                 || scanMode == SCAN_MODE_BALANCED
151                 || scanMode == SCAN_MODE_LOW_POWER
152                 || scanMode == SCAN_MODE_NO_POWER;
153     }
154 
155     /**
156      * Returns the scan type for this request.
157      */
getScanType()158     public @ScanType int getScanType() {
159         return mScanType;
160     }
161 
162     /**
163      * Returns the scan mode for this request.
164      */
getScanMode()165     public @ScanMode int getScanMode() {
166         return mScanMode;
167     }
168 
169     /**
170      * Returns if Bluetooth Low Energy enabled for scanning.
171      */
isBleEnabled()172     public boolean isBleEnabled() {
173         return mBleEnabled;
174     }
175 
176     /**
177      * Returns if CHRE enabled for scanning.
178      */
isOffloadOnly()179     public boolean isOffloadOnly() {
180         return mOffloadOnly;
181     }
182 
183     /**
184      * Returns Scan Filters for this request.
185      */
186     @NonNull
getScanFilters()187     public List<ScanFilter> getScanFilters() {
188         return mScanFilters;
189     }
190 
191     /**
192      * Returns the work source used for power attribution of this request.
193      *
194      * @hide
195      */
196     @SystemApi
197     @NonNull
getWorkSource()198     public WorkSource getWorkSource() {
199         return mWorkSource;
200     }
201 
202     /**
203      * No special parcel contents.
204      */
205     @Override
describeContents()206     public int describeContents() {
207         return 0;
208     }
209 
210     /**
211      * Returns a string representation of this ScanRequest.
212      */
213     @Override
toString()214     public String toString() {
215         StringBuilder stringBuilder = new StringBuilder();
216         stringBuilder.append("Request[")
217                 .append("scanType=").append(mScanType);
218         stringBuilder.append(", scanMode=").append(scanModeToString(mScanMode));
219         // TODO(b/286137024): Remove this when CTS R5 is rolled out.
220         if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) {
221             stringBuilder.append(", bleEnabled=").append(mBleEnabled);
222             stringBuilder.append(", offloadOnly=").append(mOffloadOnly);
223         } else {
224             stringBuilder.append(", enableBle=").append(mBleEnabled);
225         }
226         stringBuilder.append(", workSource=").append(mWorkSource);
227         stringBuilder.append(", scanFilters=").append(mScanFilters);
228         stringBuilder.append("]");
229         return stringBuilder.toString();
230     }
231 
232     @Override
writeToParcel(@onNull Parcel dest, int flags)233     public void writeToParcel(@NonNull Parcel dest, int flags) {
234         dest.writeInt(mScanType);
235         dest.writeInt(mScanMode);
236         dest.writeBoolean(mBleEnabled);
237         dest.writeBoolean(mOffloadOnly);
238         dest.writeTypedObject(mWorkSource, /* parcelableFlags= */0);
239         final int size = mScanFilters.size();
240         dest.writeInt(size);
241         for (int i = 0; i < size; i++) {
242             mScanFilters.get(i).writeToParcel(dest, flags);
243         }
244     }
245 
246     @Override
equals(Object other)247     public boolean equals(Object other) {
248         if (other instanceof ScanRequest) {
249             ScanRequest otherRequest = (ScanRequest) other;
250             return mScanType == otherRequest.mScanType
251                     && (mScanMode == otherRequest.mScanMode)
252                     && (mBleEnabled == otherRequest.mBleEnabled)
253                     && (mOffloadOnly == otherRequest.mOffloadOnly)
254                     && (Objects.equals(mWorkSource, otherRequest.mWorkSource));
255         }
256         return false;
257     }
258 
259     @Override
hashCode()260     public int hashCode() {
261         return Objects.hash(mScanType, mScanMode, mBleEnabled, mOffloadOnly, mWorkSource);
262     }
263 
264     /** @hide **/
265     @Retention(RetentionPolicy.SOURCE)
266     @IntDef({SCAN_TYPE_FAST_PAIR, SCAN_TYPE_NEARBY_PRESENCE})
267     public @interface ScanType {
268     }
269 
270     /** @hide **/
271     @Retention(RetentionPolicy.SOURCE)
272     @IntDef({SCAN_MODE_LOW_LATENCY, SCAN_MODE_BALANCED,
273             SCAN_MODE_LOW_POWER,
274             SCAN_MODE_NO_POWER})
275     public @interface ScanMode {}
276 
277     /** A builder class for {@link ScanRequest}. */
278     public static final class Builder {
279         private static final int INVALID_SCAN_TYPE = -1;
280         private @ScanType int mScanType;
281         private @ScanMode int mScanMode;
282 
283         private boolean mBleEnabled;
284         private boolean mOffloadOnly;
285         private WorkSource mWorkSource;
286         private List<ScanFilter> mScanFilters;
287 
288         /** Creates a new Builder with the given scan type. */
Builder()289         public Builder() {
290             mScanType = INVALID_SCAN_TYPE;
291             mBleEnabled = true;
292             mOffloadOnly = false;
293             mWorkSource = new WorkSource();
294             mScanFilters = new ArrayList<>();
295         }
296 
297         /**
298          * Sets the scan type for the request. The scan type must be one of the SCAN_TYPE_ constants
299          * in {@link ScanRequest}.
300          *
301          * @param scanType The scan type for the request
302          */
303         @NonNull
setScanType(@canType int scanType)304         public Builder setScanType(@ScanType int scanType) {
305             mScanType = scanType;
306             return this;
307         }
308 
309         /**
310          * Sets the scan mode for the request. The scan type must be one of the SCAN_MODE_ constants
311          * in {@link ScanRequest}.
312          *
313          * @param scanMode The scan mode for the request
314          */
315         @NonNull
setScanMode(@canMode int scanMode)316         public Builder setScanMode(@ScanMode int scanMode) {
317             mScanMode = scanMode;
318             return this;
319         }
320 
321         /**
322          * Sets if the ble is enabled for scanning.
323          *
324          * @param bleEnabled If the BluetoothLe is enabled in the device.
325          */
326         @NonNull
setBleEnabled(boolean bleEnabled)327         public Builder setBleEnabled(boolean bleEnabled) {
328             mBleEnabled = bleEnabled;
329             return this;
330         }
331 
332         /**
333          * By default, a scan request can be served by either offload or
334          * non-offload implementation, depending on the resource available in the device.
335          *
336          * A client can explicitly request a scan to be served by offload only.
337          * Before the request, the client should query the offload capability by
338          * using {@link NearbyManager#queryOffloadCapability(Executor, Consumer)}}. Otherwise,
339          * {@link ScanCallback#ERROR_UNSUPPORTED} will be returned on devices without
340          * offload capability.
341          */
342         @NonNull
setOffloadOnly(boolean offloadOnly)343         public Builder setOffloadOnly(boolean offloadOnly) {
344             mOffloadOnly = offloadOnly;
345             return this;
346         }
347 
348         /**
349          * Sets the work source to use for power attribution for this scan request. Defaults to
350          * empty work source, which implies the caller that sends the scan request will be used
351          * for power attribution.
352          *
353          * <p>Permission enforcement occurs when the resulting scan request is used, not when
354          * this method is invoked.
355          *
356          * @param workSource identifying the application(s) for which to blame for the scan.
357          * @hide
358          */
359         @RequiresPermission(Manifest.permission.UPDATE_DEVICE_STATS)
360         @NonNull
361         @SystemApi
setWorkSource(@ullable WorkSource workSource)362         public Builder setWorkSource(@Nullable WorkSource workSource) {
363             if (workSource == null) {
364                 mWorkSource = new WorkSource();
365             } else {
366                 mWorkSource = workSource;
367             }
368             return this;
369         }
370 
371         /**
372          * Adds a scan filter to the request. Client can call this method multiple times to add
373          * more than one scan filter. Scan results that match any of these scan filters will
374          * be returned.
375          *
376          * <p>On devices with hardware support, scan filters can significantly improve the battery
377          * usage of Nearby scans.
378          *
379          * @param scanFilter Filter for scanning the request.
380          */
381         @NonNull
addScanFilter(@onNull ScanFilter scanFilter)382         public Builder addScanFilter(@NonNull ScanFilter scanFilter) {
383             Objects.requireNonNull(scanFilter);
384             mScanFilters.add(scanFilter);
385             return this;
386         }
387 
388         /**
389          * Builds a scan request from this builder.
390          *
391          * @return a new nearby scan request.
392          * @throws IllegalStateException if the scanType is not one of the SCAN_TYPE_ constants in
393          *                               {@link ScanRequest}.
394          */
395         @NonNull
build()396         public ScanRequest build() {
397             Preconditions.checkState(isValidScanType(mScanType),
398                     "invalid scan type : " + mScanType
399                             + ", scan type must be one of ScanRequest#SCAN_TYPE_");
400             Preconditions.checkState(isValidScanMode(mScanMode),
401                     "invalid scan mode : " + mScanMode
402                             + ", scan mode must be one of ScanMode#SCAN_MODE_");
403             return new ScanRequest(
404                     mScanType, mScanMode, mBleEnabled, mOffloadOnly, mWorkSource, mScanFilters);
405         }
406     }
407 }
408