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