blob: c62e3e301242c1a6c4cd62902f9fafbafe00fc83 [file] [log] [blame]
/*
* Copyright (C) 2021 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package android.nearby;
import android.Manifest;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.RequiresPermission;
import android.annotation.SystemApi;
import android.os.Parcel;
import android.os.Parcelable;
import android.os.WorkSource;
import com.android.internal.util.Preconditions;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.Executor;
import java.util.function.Consumer;
/**
* An encapsulation of various parameters for requesting nearby scans.
*
* @hide
*/
@SystemApi
public final class ScanRequest implements Parcelable {
/** Scan type for scanning devices using fast pair protocol. */
public static final int SCAN_TYPE_FAST_PAIR = 1;
/** Scan type for scanning devices using nearby presence protocol. */
public static final int SCAN_TYPE_NEARBY_PRESENCE = 2;
/** Scan mode uses highest duty cycle. */
public static final int SCAN_MODE_LOW_LATENCY = 2;
/** Scan in balanced power mode.
* Scan results are returned at a rate that provides a good trade-off between scan
* frequency and power consumption.
*/
public static final int SCAN_MODE_BALANCED = 1;
/** Perform scan in low power mode. This is the default scan mode. */
public static final int SCAN_MODE_LOW_POWER = 0;
/**
* A special scan mode. Applications using this scan mode will passively listen for other scan
* results without starting BLE scans themselves.
*/
public static final int SCAN_MODE_NO_POWER = -1;
/**
* A special scan mode to indicate that client only wants to use CHRE to scan.
*
* @hide
*/
public static final int SCAN_MODE_CHRE_ONLY = 3;
/**
* Used to read a ScanRequest from a Parcel.
*/
@NonNull
public static final Creator<ScanRequest> CREATOR = new Creator<ScanRequest>() {
@Override
public ScanRequest createFromParcel(Parcel in) {
ScanRequest.Builder builder = new ScanRequest.Builder()
.setScanType(in.readInt())
.setScanMode(in.readInt())
.setBleEnabled(in.readBoolean())
.setOffloadOnly(in.readBoolean())
.setWorkSource(in.readTypedObject(WorkSource.CREATOR));
final int size = in.readInt();
for (int i = 0; i < size; i++) {
builder.addScanFilter(ScanFilter.createFromParcel(in));
}
return builder.build();
}
@Override
public ScanRequest[] newArray(int size) {
return new ScanRequest[size];
}
};
private final @ScanType int mScanType;
private final @ScanMode int mScanMode;
private final boolean mBleEnabled;
private final boolean mOffloadOnly;
private final @NonNull WorkSource mWorkSource;
private final List<ScanFilter> mScanFilters;
private ScanRequest(@ScanType int scanType, @ScanMode int scanMode, boolean bleEnabled,
boolean offloadOnly, @NonNull WorkSource workSource, List<ScanFilter> scanFilters) {
mScanType = scanType;
mScanMode = scanMode;
mBleEnabled = bleEnabled;
mOffloadOnly = offloadOnly;
mWorkSource = workSource;
mScanFilters = scanFilters;
}
/**
* Convert scan mode to readable string.
*
* @param scanMode Integer that may represent a{@link ScanMode}.
*/
@NonNull
public static String scanModeToString(@ScanMode int scanMode) {
switch (scanMode) {
case SCAN_MODE_LOW_LATENCY:
return "SCAN_MODE_LOW_LATENCY";
case SCAN_MODE_BALANCED:
return "SCAN_MODE_BALANCED";
case SCAN_MODE_LOW_POWER:
return "SCAN_MODE_LOW_POWER";
case SCAN_MODE_NO_POWER:
return "SCAN_MODE_NO_POWER";
default:
return "SCAN_MODE_INVALID";
}
}
/**
* Returns true if an integer is a defined scan type.
*/
public static boolean isValidScanType(@ScanType int scanType) {
return scanType == SCAN_TYPE_FAST_PAIR
|| scanType == SCAN_TYPE_NEARBY_PRESENCE;
}
/**
* Returns true if an integer is a defined scan mode.
*/
public static boolean isValidScanMode(@ScanMode int scanMode) {
return scanMode == SCAN_MODE_LOW_LATENCY
|| scanMode == SCAN_MODE_BALANCED
|| scanMode == SCAN_MODE_LOW_POWER
|| scanMode == SCAN_MODE_NO_POWER;
}
/**
* Returns the scan type for this request.
*/
public @ScanType int getScanType() {
return mScanType;
}
/**
* Returns the scan mode for this request.
*/
public @ScanMode int getScanMode() {
return mScanMode;
}
/**
* Returns if Bluetooth Low Energy enabled for scanning.
*/
public boolean isBleEnabled() {
return mBleEnabled;
}
/**
* Returns if CHRE enabled for scanning.
*/
public boolean isOffloadOnly() {
return mOffloadOnly;
}
/**
* Returns Scan Filters for this request.
*/
@NonNull
public List<ScanFilter> getScanFilters() {
return mScanFilters;
}
/**
* Returns the work source used for power attribution of this request.
*
* @hide
*/
@SystemApi
@NonNull
public WorkSource getWorkSource() {
return mWorkSource;
}
/**
* No special parcel contents.
*/
@Override
public int describeContents() {
return 0;
}
/**
* Returns a string representation of this ScanRequest.
*/
@Override
public String toString() {
StringBuilder stringBuilder = new StringBuilder();
stringBuilder.append("Request[")
.append("scanType=").append(mScanType);
stringBuilder.append(", scanMode=").append(scanModeToString(mScanMode));
stringBuilder.append(", bleEnabled=").append(mBleEnabled);
stringBuilder.append(", offloadOnly=").append(mOffloadOnly);
stringBuilder.append(", workSource=").append(mWorkSource);
stringBuilder.append(", scanFilters=").append(mScanFilters);
stringBuilder.append("]");
return stringBuilder.toString();
}
@Override
public void writeToParcel(@NonNull Parcel dest, int flags) {
dest.writeInt(mScanType);
dest.writeInt(mScanMode);
dest.writeBoolean(mBleEnabled);
dest.writeBoolean(mOffloadOnly);
dest.writeTypedObject(mWorkSource, /* parcelableFlags= */0);
final int size = mScanFilters.size();
dest.writeInt(size);
for (int i = 0; i < size; i++) {
mScanFilters.get(i).writeToParcel(dest, flags);
}
}
@Override
public boolean equals(Object other) {
if (other instanceof ScanRequest) {
ScanRequest otherRequest = (ScanRequest) other;
return mScanType == otherRequest.mScanType
&& (mScanMode == otherRequest.mScanMode)
&& (mBleEnabled == otherRequest.mBleEnabled)
&& (mOffloadOnly == otherRequest.mOffloadOnly)
&& (Objects.equals(mWorkSource, otherRequest.mWorkSource));
}
return false;
}
@Override
public int hashCode() {
return Objects.hash(mScanType, mScanMode, mBleEnabled, mOffloadOnly, mWorkSource);
}
/** @hide **/
@Retention(RetentionPolicy.SOURCE)
@IntDef({SCAN_TYPE_FAST_PAIR, SCAN_TYPE_NEARBY_PRESENCE})
public @interface ScanType {
}
/** @hide **/
@Retention(RetentionPolicy.SOURCE)
@IntDef({SCAN_MODE_LOW_LATENCY, SCAN_MODE_BALANCED,
SCAN_MODE_LOW_POWER,
SCAN_MODE_NO_POWER})
public @interface ScanMode {}
/** A builder class for {@link ScanRequest}. */
public static final class Builder {
private static final int INVALID_SCAN_TYPE = -1;
private @ScanType int mScanType;
private @ScanMode int mScanMode;
private boolean mBleEnabled;
private boolean mOffloadOnly;
private WorkSource mWorkSource;
private List<ScanFilter> mScanFilters;
/** Creates a new Builder with the given scan type. */
public Builder() {
mScanType = INVALID_SCAN_TYPE;
mBleEnabled = true;
mOffloadOnly = false;
mWorkSource = new WorkSource();
mScanFilters = new ArrayList<>();
}
/**
* Sets the scan type for the request. The scan type must be one of the SCAN_TYPE_ constants
* in {@link ScanRequest}.
*
* @param scanType The scan type for the request
*/
@NonNull
public Builder setScanType(@ScanType int scanType) {
mScanType = scanType;
return this;
}
/**
* Sets the scan mode for the request. The scan type must be one of the SCAN_MODE_ constants
* in {@link ScanRequest}.
*
* @param scanMode The scan mode for the request
*/
@NonNull
public Builder setScanMode(@ScanMode int scanMode) {
mScanMode = scanMode;
return this;
}
/**
* Sets if the ble is enabled for scanning.
*
* @param bleEnabled If the BluetoothLe is enabled in the device.
*/
@NonNull
public Builder setBleEnabled(boolean bleEnabled) {
mBleEnabled = bleEnabled;
return this;
}
/**
* By default, a scan request can be served by either offload or
* non-offload implementation, depending on the resource available in the device.
*
* A client can explicitly request a scan to be served by offload only.
* Before the request, the client should query the offload capability by
* using {@link NearbyManager#queryOffloadCapability(Executor, Consumer)}}. Otherwise,
* {@link ScanCallback#ERROR_UNSUPPORTED} will be returned on devices without
* offload capability.
*/
@NonNull
public Builder setOffloadOnly(boolean offloadOnly) {
mOffloadOnly = offloadOnly;
return this;
}
/**
* Sets the work source to use for power attribution for this scan request. Defaults to
* empty work source, which implies the caller that sends the scan request will be used
* for power attribution.
*
* <p>Permission enforcement occurs when the resulting scan request is used, not when
* this method is invoked.
*
* @param workSource identifying the application(s) for which to blame for the scan.
* @hide
*/
@RequiresPermission(Manifest.permission.UPDATE_DEVICE_STATS)
@NonNull
@SystemApi
public Builder setWorkSource(@Nullable WorkSource workSource) {
if (workSource == null) {
mWorkSource = new WorkSource();
} else {
mWorkSource = workSource;
}
return this;
}
/**
* Adds a scan filter to the request. Client can call this method multiple times to add
* more than one scan filter. Scan results that match any of these scan filters will
* be returned.
*
* <p>On devices with hardware support, scan filters can significantly improve the battery
* usage of Nearby scans.
*
* @param scanFilter Filter for scanning the request.
*/
@NonNull
public Builder addScanFilter(@NonNull ScanFilter scanFilter) {
Objects.requireNonNull(scanFilter);
mScanFilters.add(scanFilter);
return this;
}
/**
* Builds a scan request from this builder.
*
* @return a new nearby scan request.
* @throws IllegalStateException if the scanType is not one of the SCAN_TYPE_ constants in
* {@link ScanRequest}.
*/
@NonNull
public ScanRequest build() {
Preconditions.checkState(isValidScanType(mScanType),
"invalid scan type : " + mScanType
+ ", scan type must be one of ScanRequest#SCAN_TYPE_");
Preconditions.checkState(isValidScanMode(mScanMode),
"invalid scan mode : " + mScanMode
+ ", scan mode must be one of ScanMode#SCAN_MODE_");
return new ScanRequest(
mScanType, mScanMode, mBleEnabled, mOffloadOnly, mWorkSource, mScanFilters);
}
}
}