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