Eugene Susla | 6ed45d8 | 2017-01-22 13:52:51 -0800 | [diff] [blame] | 1 | /* |
| 2 | * Copyright (C) 2017 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.companion; |
| 18 | |
Eugene Susla | 36e866b | 2017-02-23 18:24:39 -0800 | [diff] [blame] | 19 | import static android.companion.BluetoothDeviceFilterUtils.getDeviceDisplayNameInternal; |
Eugene Susla | 6ed45d8 | 2017-01-22 13:52:51 -0800 | [diff] [blame] | 20 | import static android.companion.BluetoothDeviceFilterUtils.patternFromString; |
| 21 | import static android.companion.BluetoothDeviceFilterUtils.patternToString; |
| 22 | |
Eugene Susla | 36e866b | 2017-02-23 18:24:39 -0800 | [diff] [blame] | 23 | import static com.android.internal.util.Preconditions.checkArgument; |
Eugene Susla | 75fb821 | 2017-04-05 10:36:24 -0700 | [diff] [blame] | 24 | import static com.android.internal.util.Preconditions.checkState; |
Eugene Susla | 36e866b | 2017-02-23 18:24:39 -0800 | [diff] [blame] | 25 | |
Eugene Susla | 6ed45d8 | 2017-01-22 13:52:51 -0800 | [diff] [blame] | 26 | import android.annotation.NonNull; |
| 27 | import android.annotation.Nullable; |
Eugene Susla | 6ed45d8 | 2017-01-22 13:52:51 -0800 | [diff] [blame] | 28 | import android.bluetooth.BluetoothDevice; |
| 29 | import android.bluetooth.le.ScanFilter; |
Eugene Susla | 36e866b | 2017-02-23 18:24:39 -0800 | [diff] [blame] | 30 | import android.bluetooth.le.ScanRecord; |
| 31 | import android.bluetooth.le.ScanResult; |
Eugene Susla | 6ed45d8 | 2017-01-22 13:52:51 -0800 | [diff] [blame] | 32 | import android.os.Parcel; |
| 33 | import android.provider.OneTimeUseBuilder; |
Eugene Susla | 36e866b | 2017-02-23 18:24:39 -0800 | [diff] [blame] | 34 | import android.text.TextUtils; |
Eugene Susla | 3c9aa17 | 2017-03-28 15:04:12 -0700 | [diff] [blame] | 35 | import android.util.Log; |
Eugene Susla | 6ed45d8 | 2017-01-22 13:52:51 -0800 | [diff] [blame] | 36 | |
Eugene Susla | 36e866b | 2017-02-23 18:24:39 -0800 | [diff] [blame] | 37 | import com.android.internal.util.BitUtils; |
Eugene Susla | 6ed45d8 | 2017-01-22 13:52:51 -0800 | [diff] [blame] | 38 | import com.android.internal.util.ObjectUtils; |
Eugene Susla | 36e866b | 2017-02-23 18:24:39 -0800 | [diff] [blame] | 39 | import com.android.internal.util.Preconditions; |
Eugene Susla | 6ed45d8 | 2017-01-22 13:52:51 -0800 | [diff] [blame] | 40 | |
Eugene Susla | a38fbf6 | 2017-03-14 10:26:10 -0700 | [diff] [blame] | 41 | import java.util.Arrays; |
| 42 | import java.util.Objects; |
Eugene Susla | 6ed45d8 | 2017-01-22 13:52:51 -0800 | [diff] [blame] | 43 | import java.util.regex.Pattern; |
| 44 | |
| 45 | /** |
| 46 | * A filter for Bluetooth LE devices |
| 47 | * |
| 48 | * @see ScanFilter |
| 49 | */ |
Eugene Susla | 36e866b | 2017-02-23 18:24:39 -0800 | [diff] [blame] | 50 | public final class BluetoothLEDeviceFilter implements DeviceFilter<ScanResult> { |
Eugene Susla | 6ed45d8 | 2017-01-22 13:52:51 -0800 | [diff] [blame] | 51 | |
Eugene Susla | 3c9aa17 | 2017-03-28 15:04:12 -0700 | [diff] [blame] | 52 | private static final boolean DEBUG = false; |
| 53 | private static final String LOG_TAG = "BluetoothLEDeviceFilter"; |
| 54 | |
Eugene Susla | 36e866b | 2017-02-23 18:24:39 -0800 | [diff] [blame] | 55 | private static final int RENAME_PREFIX_LENGTH_LIMIT = 10; |
Eugene Susla | 6ed45d8 | 2017-01-22 13:52:51 -0800 | [diff] [blame] | 56 | |
| 57 | private final Pattern mNamePattern; |
| 58 | private final ScanFilter mScanFilter; |
Eugene Susla | 36e866b | 2017-02-23 18:24:39 -0800 | [diff] [blame] | 59 | private final byte[] mRawDataFilter; |
| 60 | private final byte[] mRawDataFilterMask; |
| 61 | private final String mRenamePrefix; |
| 62 | private final String mRenameSuffix; |
| 63 | private final int mRenameBytesFrom; |
| 64 | private final int mRenameBytesTo; |
Eugene Susla | 75fb821 | 2017-04-05 10:36:24 -0700 | [diff] [blame] | 65 | private final int mRenameNameFrom; |
| 66 | private final int mRenameNameTo; |
Eugene Susla | 36e866b | 2017-02-23 18:24:39 -0800 | [diff] [blame] | 67 | private final boolean mRenameBytesReverseOrder; |
Eugene Susla | 6ed45d8 | 2017-01-22 13:52:51 -0800 | [diff] [blame] | 68 | |
Eugene Susla | 36e866b | 2017-02-23 18:24:39 -0800 | [diff] [blame] | 69 | private BluetoothLEDeviceFilter(Pattern namePattern, ScanFilter scanFilter, |
| 70 | byte[] rawDataFilter, byte[] rawDataFilterMask, String renamePrefix, |
| 71 | String renameSuffix, int renameBytesFrom, int renameBytesTo, |
Eugene Susla | 75fb821 | 2017-04-05 10:36:24 -0700 | [diff] [blame] | 72 | int renameNameFrom, int renameNameTo, boolean renameBytesReverseOrder) { |
Eugene Susla | 6ed45d8 | 2017-01-22 13:52:51 -0800 | [diff] [blame] | 73 | mNamePattern = namePattern; |
| 74 | mScanFilter = ObjectUtils.firstNotNull(scanFilter, ScanFilter.EMPTY); |
Eugene Susla | 36e866b | 2017-02-23 18:24:39 -0800 | [diff] [blame] | 75 | mRawDataFilter = rawDataFilter; |
| 76 | mRawDataFilterMask = rawDataFilterMask; |
| 77 | mRenamePrefix = renamePrefix; |
| 78 | mRenameSuffix = renameSuffix; |
| 79 | mRenameBytesFrom = renameBytesFrom; |
| 80 | mRenameBytesTo = renameBytesTo; |
Eugene Susla | 75fb821 | 2017-04-05 10:36:24 -0700 | [diff] [blame] | 81 | mRenameNameFrom = renameNameFrom; |
| 82 | mRenameNameTo = renameNameTo; |
Eugene Susla | 36e866b | 2017-02-23 18:24:39 -0800 | [diff] [blame] | 83 | mRenameBytesReverseOrder = renameBytesReverseOrder; |
Eugene Susla | 6ed45d8 | 2017-01-22 13:52:51 -0800 | [diff] [blame] | 84 | } |
| 85 | |
| 86 | /** @hide */ |
| 87 | @Nullable |
| 88 | public Pattern getNamePattern() { |
| 89 | return mNamePattern; |
| 90 | } |
| 91 | |
| 92 | /** @hide */ |
| 93 | @NonNull |
| 94 | public ScanFilter getScanFilter() { |
| 95 | return mScanFilter; |
| 96 | } |
| 97 | |
| 98 | /** @hide */ |
Eugene Susla | 36e866b | 2017-02-23 18:24:39 -0800 | [diff] [blame] | 99 | @Nullable |
| 100 | public byte[] getRawDataFilter() { |
| 101 | return mRawDataFilter; |
| 102 | } |
| 103 | |
| 104 | /** @hide */ |
| 105 | @Nullable |
| 106 | public byte[] getRawDataFilterMask() { |
| 107 | return mRawDataFilterMask; |
| 108 | } |
| 109 | |
| 110 | /** @hide */ |
| 111 | @Nullable |
| 112 | public String getRenamePrefix() { |
| 113 | return mRenamePrefix; |
| 114 | } |
| 115 | |
| 116 | /** @hide */ |
| 117 | @Nullable |
| 118 | public String getRenameSuffix() { |
| 119 | return mRenameSuffix; |
| 120 | } |
| 121 | |
| 122 | /** @hide */ |
| 123 | public int getRenameBytesFrom() { |
| 124 | return mRenameBytesFrom; |
| 125 | } |
| 126 | |
| 127 | /** @hide */ |
| 128 | public int getRenameBytesTo() { |
| 129 | return mRenameBytesTo; |
| 130 | } |
| 131 | |
| 132 | /** @hide */ |
| 133 | public boolean isRenameBytesReverseOrder() { |
| 134 | return mRenameBytesReverseOrder; |
| 135 | } |
| 136 | |
| 137 | /** @hide */ |
Eugene Susla | 6ed45d8 | 2017-01-22 13:52:51 -0800 | [diff] [blame] | 138 | @Override |
Eugene Susla | 36e866b | 2017-02-23 18:24:39 -0800 | [diff] [blame] | 139 | @Nullable |
| 140 | public String getDeviceDisplayName(ScanResult sr) { |
Eugene Susla | 75fb821 | 2017-04-05 10:36:24 -0700 | [diff] [blame] | 141 | if (mRenameBytesFrom < 0 && mRenameNameFrom < 0) { |
| 142 | return getDeviceDisplayNameInternal(sr.getDevice()); |
| 143 | } |
Eugene Susla | 36e866b | 2017-02-23 18:24:39 -0800 | [diff] [blame] | 144 | final StringBuilder sb = new StringBuilder(TextUtils.emptyIfNull(mRenamePrefix)); |
Eugene Susla | 75fb821 | 2017-04-05 10:36:24 -0700 | [diff] [blame] | 145 | if (mRenameBytesFrom >= 0) { |
| 146 | final byte[] bytes = sr.getScanRecord().getBytes(); |
| 147 | int startInclusive = mRenameBytesFrom; |
| 148 | int endInclusive = mRenameBytesTo - 1; |
| 149 | int initial = mRenameBytesReverseOrder ? endInclusive : startInclusive; |
| 150 | int step = mRenameBytesReverseOrder ? -1 : 1; |
| 151 | for (int i = initial; startInclusive <= i && i <= endInclusive; i += step) { |
| 152 | sb.append(Byte.toHexString(bytes[i], true)); |
| 153 | } |
| 154 | } else { |
| 155 | sb.append( |
| 156 | getDeviceDisplayNameInternal(sr.getDevice()) |
| 157 | .substring(mRenameNameFrom, mRenameNameTo)); |
Eugene Susla | 36e866b | 2017-02-23 18:24:39 -0800 | [diff] [blame] | 158 | } |
| 159 | return sb.append(TextUtils.emptyIfNull(mRenameSuffix)).toString(); |
| 160 | } |
| 161 | |
| 162 | /** @hide */ |
| 163 | @Override |
| 164 | public boolean matches(ScanResult device) { |
Eugene Susla | 3c9aa17 | 2017-03-28 15:04:12 -0700 | [diff] [blame] | 165 | boolean result = matches(device.getDevice()) |
| 166 | && (mRawDataFilter == null |
| 167 | || BitUtils.maskedEquals(device.getScanRecord().getBytes(), |
| 168 | mRawDataFilter, mRawDataFilterMask)); |
| 169 | if (DEBUG) Log.i(LOG_TAG, "matches(this = " + this + ", device = " + device + |
| 170 | ") -> " + result); |
| 171 | return result; |
Eugene Susla | 36e866b | 2017-02-23 18:24:39 -0800 | [diff] [blame] | 172 | } |
| 173 | |
| 174 | private boolean matches(BluetoothDevice device) { |
Eugene Susla | 6ed45d8 | 2017-01-22 13:52:51 -0800 | [diff] [blame] | 175 | return BluetoothDeviceFilterUtils.matches(getScanFilter(), device) |
| 176 | && BluetoothDeviceFilterUtils.matchesName(getNamePattern(), device); |
| 177 | } |
| 178 | |
Eugene Susla | 36e866b | 2017-02-23 18:24:39 -0800 | [diff] [blame] | 179 | /** @hide */ |
| 180 | @Override |
| 181 | public int getMediumType() { |
| 182 | return DeviceFilter.MEDIUM_TYPE_BLUETOOTH_LE; |
| 183 | } |
| 184 | |
Eugene Susla | 6ed45d8 | 2017-01-22 13:52:51 -0800 | [diff] [blame] | 185 | @Override |
Eugene Susla | a38fbf6 | 2017-03-14 10:26:10 -0700 | [diff] [blame] | 186 | public boolean equals(Object o) { |
| 187 | if (this == o) return true; |
| 188 | if (o == null || getClass() != o.getClass()) return false; |
| 189 | BluetoothLEDeviceFilter that = (BluetoothLEDeviceFilter) o; |
| 190 | return mRenameBytesFrom == that.mRenameBytesFrom && |
| 191 | mRenameBytesTo == that.mRenameBytesTo && |
| 192 | mRenameBytesReverseOrder == that.mRenameBytesReverseOrder && |
| 193 | Objects.equals(mNamePattern, that.mNamePattern) && |
| 194 | Objects.equals(mScanFilter, that.mScanFilter) && |
| 195 | Arrays.equals(mRawDataFilter, that.mRawDataFilter) && |
| 196 | Arrays.equals(mRawDataFilterMask, that.mRawDataFilterMask) && |
| 197 | Objects.equals(mRenamePrefix, that.mRenamePrefix) && |
| 198 | Objects.equals(mRenameSuffix, that.mRenameSuffix); |
| 199 | } |
| 200 | |
| 201 | @Override |
| 202 | public int hashCode() { |
| 203 | return Objects.hash(mNamePattern, mScanFilter, mRawDataFilter, mRawDataFilterMask, |
| 204 | mRenamePrefix, mRenameSuffix, mRenameBytesFrom, mRenameBytesTo, |
| 205 | mRenameBytesReverseOrder); |
| 206 | } |
| 207 | |
| 208 | @Override |
Eugene Susla | 6ed45d8 | 2017-01-22 13:52:51 -0800 | [diff] [blame] | 209 | public void writeToParcel(Parcel dest, int flags) { |
| 210 | dest.writeString(patternToString(getNamePattern())); |
| 211 | dest.writeParcelable(mScanFilter, flags); |
Eugene Susla | a38fbf6 | 2017-03-14 10:26:10 -0700 | [diff] [blame] | 212 | dest.writeByteArray(mRawDataFilter); |
| 213 | dest.writeByteArray(mRawDataFilterMask); |
| 214 | dest.writeString(mRenamePrefix); |
| 215 | dest.writeString(mRenameSuffix); |
| 216 | dest.writeInt(mRenameBytesFrom); |
| 217 | dest.writeInt(mRenameBytesTo); |
Eugene Susla | 75fb821 | 2017-04-05 10:36:24 -0700 | [diff] [blame] | 218 | dest.writeInt(mRenameNameFrom); |
| 219 | dest.writeInt(mRenameNameTo); |
Eugene Susla | a38fbf6 | 2017-03-14 10:26:10 -0700 | [diff] [blame] | 220 | dest.writeBoolean(mRenameBytesReverseOrder); |
Eugene Susla | 6ed45d8 | 2017-01-22 13:52:51 -0800 | [diff] [blame] | 221 | } |
| 222 | |
| 223 | @Override |
| 224 | public int describeContents() { |
| 225 | return 0; |
| 226 | } |
| 227 | |
Eugene Susla | a7717e3 | 2017-04-17 19:13:31 -0700 | [diff] [blame] | 228 | @Override |
| 229 | public String toString() { |
| 230 | return "BluetoothLEDeviceFilter{" + |
| 231 | "mNamePattern=" + mNamePattern + |
| 232 | ", mScanFilter=" + mScanFilter + |
| 233 | ", mRawDataFilter=" + Arrays.toString(mRawDataFilter) + |
| 234 | ", mRawDataFilterMask=" + Arrays.toString(mRawDataFilterMask) + |
| 235 | ", mRenamePrefix='" + mRenamePrefix + '\'' + |
| 236 | ", mRenameSuffix='" + mRenameSuffix + '\'' + |
| 237 | ", mRenameBytesFrom=" + mRenameBytesFrom + |
| 238 | ", mRenameBytesTo=" + mRenameBytesTo + |
| 239 | ", mRenameNameFrom=" + mRenameNameFrom + |
| 240 | ", mRenameNameTo=" + mRenameNameTo + |
| 241 | ", mRenameBytesReverseOrder=" + mRenameBytesReverseOrder + |
| 242 | '}'; |
| 243 | } |
| 244 | |
Eugene Susla | 6ed45d8 | 2017-01-22 13:52:51 -0800 | [diff] [blame] | 245 | public static final Creator<BluetoothLEDeviceFilter> CREATOR |
| 246 | = new Creator<BluetoothLEDeviceFilter>() { |
| 247 | @Override |
| 248 | public BluetoothLEDeviceFilter createFromParcel(Parcel in) { |
Eugene Susla | a38fbf6 | 2017-03-14 10:26:10 -0700 | [diff] [blame] | 249 | Builder builder = new Builder() |
Eugene Susla | 36e866b | 2017-02-23 18:24:39 -0800 | [diff] [blame] | 250 | .setNamePattern(patternFromString(in.readString())) |
Eugene Susla | a38fbf6 | 2017-03-14 10:26:10 -0700 | [diff] [blame] | 251 | .setScanFilter(in.readParcelable(null)); |
| 252 | byte[] rawDataFilter = in.createByteArray(); |
| 253 | byte[] rawDataFilterMask = in.createByteArray(); |
| 254 | if (rawDataFilter != null) { |
| 255 | builder.setRawDataFilter(rawDataFilter, rawDataFilterMask); |
| 256 | } |
| 257 | String renamePrefix = in.readString(); |
| 258 | String suffix = in.readString(); |
| 259 | int bytesFrom = in.readInt(); |
| 260 | int bytesTo = in.readInt(); |
Eugene Susla | 75fb821 | 2017-04-05 10:36:24 -0700 | [diff] [blame] | 261 | int nameFrom = in.readInt(); |
| 262 | int nameTo = in.readInt(); |
Eugene Susla | a38fbf6 | 2017-03-14 10:26:10 -0700 | [diff] [blame] | 263 | boolean bytesReverseOrder = in.readBoolean(); |
| 264 | if (renamePrefix != null) { |
Eugene Susla | 75fb821 | 2017-04-05 10:36:24 -0700 | [diff] [blame] | 265 | if (bytesFrom >= 0) { |
| 266 | builder.setRenameFromBytes(renamePrefix, suffix, bytesFrom, bytesTo, |
| 267 | bytesReverseOrder); |
| 268 | } else { |
| 269 | builder.setRenameFromName(renamePrefix, suffix, nameFrom, nameTo); |
| 270 | } |
Eugene Susla | a38fbf6 | 2017-03-14 10:26:10 -0700 | [diff] [blame] | 271 | } |
| 272 | return builder.build(); |
Eugene Susla | 6ed45d8 | 2017-01-22 13:52:51 -0800 | [diff] [blame] | 273 | } |
| 274 | |
| 275 | @Override |
| 276 | public BluetoothLEDeviceFilter[] newArray(int size) { |
| 277 | return new BluetoothLEDeviceFilter[size]; |
| 278 | } |
| 279 | }; |
| 280 | |
Eugene Susla | 36e866b | 2017-02-23 18:24:39 -0800 | [diff] [blame] | 281 | public static int getRenamePrefixLengthLimit() { |
| 282 | return RENAME_PREFIX_LENGTH_LIMIT; |
| 283 | } |
| 284 | |
Eugene Susla | 6ed45d8 | 2017-01-22 13:52:51 -0800 | [diff] [blame] | 285 | /** |
| 286 | * Builder for {@link BluetoothLEDeviceFilter} |
| 287 | */ |
| 288 | public static final class Builder extends OneTimeUseBuilder<BluetoothLEDeviceFilter> { |
| 289 | private ScanFilter mScanFilter; |
| 290 | private Pattern mNamePattern; |
Eugene Susla | 36e866b | 2017-02-23 18:24:39 -0800 | [diff] [blame] | 291 | private byte[] mRawDataFilter; |
| 292 | private byte[] mRawDataFilterMask; |
| 293 | private String mRenamePrefix; |
| 294 | private String mRenameSuffix; |
| 295 | private int mRenameBytesFrom = -1; |
| 296 | private int mRenameBytesTo; |
Eugene Susla | 75fb821 | 2017-04-05 10:36:24 -0700 | [diff] [blame] | 297 | private int mRenameNameFrom = -1; |
| 298 | private int mRenameNameTo; |
Eugene Susla | 36e866b | 2017-02-23 18:24:39 -0800 | [diff] [blame] | 299 | private boolean mRenameBytesReverseOrder = false; |
Eugene Susla | 6ed45d8 | 2017-01-22 13:52:51 -0800 | [diff] [blame] | 300 | |
| 301 | /** |
| 302 | * @param regex if set, only devices with {@link BluetoothDevice#getName name} matching the |
| 303 | * given regular expression will be shown |
Eugene Susla | 36e866b | 2017-02-23 18:24:39 -0800 | [diff] [blame] | 304 | * @return self for chaining |
Eugene Susla | 6ed45d8 | 2017-01-22 13:52:51 -0800 | [diff] [blame] | 305 | */ |
| 306 | public Builder setNamePattern(@Nullable Pattern regex) { |
| 307 | checkNotUsed(); |
| 308 | mNamePattern = regex; |
| 309 | return this; |
| 310 | } |
| 311 | |
| 312 | /** |
| 313 | * @param scanFilter a {@link ScanFilter} to filter devices by |
| 314 | * |
Eugene Susla | 36e866b | 2017-02-23 18:24:39 -0800 | [diff] [blame] | 315 | * @return self for chaining |
Eugene Susla | 6ed45d8 | 2017-01-22 13:52:51 -0800 | [diff] [blame] | 316 | * @see ScanFilter for specific details on its various fields |
| 317 | */ |
| 318 | @NonNull |
| 319 | public Builder setScanFilter(@Nullable ScanFilter scanFilter) { |
| 320 | checkNotUsed(); |
| 321 | mScanFilter = scanFilter; |
| 322 | return this; |
| 323 | } |
| 324 | |
Eugene Susla | 36e866b | 2017-02-23 18:24:39 -0800 | [diff] [blame] | 325 | /** |
| 326 | * Filter devices by raw advertisement data, as obtained by {@link ScanRecord#getBytes} |
| 327 | * |
| 328 | * @param rawDataFilter bit values that have to match against advertized data |
| 329 | * @param rawDataFilterMask bits that have to be matched |
| 330 | * @return self for chaining |
| 331 | */ |
| 332 | @NonNull |
| 333 | public Builder setRawDataFilter(@NonNull byte[] rawDataFilter, |
Eugene Susla | a38fbf6 | 2017-03-14 10:26:10 -0700 | [diff] [blame] | 334 | @Nullable byte[] rawDataFilterMask) { |
Eugene Susla | 36e866b | 2017-02-23 18:24:39 -0800 | [diff] [blame] | 335 | checkNotUsed(); |
Eugene Susla | a38fbf6 | 2017-03-14 10:26:10 -0700 | [diff] [blame] | 336 | Preconditions.checkNotNull(rawDataFilter); |
| 337 | checkArgument(rawDataFilterMask == null || |
| 338 | rawDataFilter.length == rawDataFilterMask.length, |
Eugene Susla | 36e866b | 2017-02-23 18:24:39 -0800 | [diff] [blame] | 339 | "Mask and filter should be the same length"); |
Eugene Susla | a38fbf6 | 2017-03-14 10:26:10 -0700 | [diff] [blame] | 340 | mRawDataFilter = rawDataFilter; |
| 341 | mRawDataFilterMask = rawDataFilterMask; |
Eugene Susla | 36e866b | 2017-02-23 18:24:39 -0800 | [diff] [blame] | 342 | return this; |
| 343 | } |
| 344 | |
| 345 | /** |
| 346 | * Rename the devices shown in the list, using specific bytes from the raw advertisement |
| 347 | * data ({@link ScanRecord#getBytes}) in hexadecimal format, as well as a custom |
| 348 | * prefix/suffix around them |
| 349 | * |
| 350 | * Note that the prefix length is limited to {@link #getRenamePrefixLengthLimit} characters |
| 351 | * to ensure that there's enough space to display the byte data |
| 352 | * |
| 353 | * The range of bytes to be displayed cannot be empty |
| 354 | * |
| 355 | * @param prefix to be displayed before the byte data |
| 356 | * @param suffix to be displayed after the byte data |
| 357 | * @param bytesFrom the start byte index to be displayed (inclusive) |
| 358 | * @param bytesTo the end byte index to be displayed (exclusive) |
| 359 | * @param bytesReverseOrder if true, the byte order of the provided range will be flipped |
| 360 | * when displaying |
| 361 | * @return self for chaining |
| 362 | */ |
| 363 | @NonNull |
Eugene Susla | 75fb821 | 2017-04-05 10:36:24 -0700 | [diff] [blame] | 364 | public Builder setRenameFromBytes(@NonNull String prefix, @NonNull String suffix, |
Eugene Susla | 36e866b | 2017-02-23 18:24:39 -0800 | [diff] [blame] | 365 | int bytesFrom, int bytesTo, boolean bytesReverseOrder) { |
Eugene Susla | 75fb821 | 2017-04-05 10:36:24 -0700 | [diff] [blame] | 366 | checkRenameNotSet(); |
| 367 | checkRangeNotEmpty(bytesFrom, bytesTo); |
Eugene Susla | 36e866b | 2017-02-23 18:24:39 -0800 | [diff] [blame] | 368 | mRenameBytesFrom = bytesFrom; |
| 369 | mRenameBytesTo = bytesTo; |
| 370 | mRenameBytesReverseOrder = bytesReverseOrder; |
Eugene Susla | 75fb821 | 2017-04-05 10:36:24 -0700 | [diff] [blame] | 371 | return setRename(prefix, suffix); |
| 372 | } |
| 373 | |
| 374 | /** |
| 375 | * Rename the devices shown in the list, using specific characters from the advertised name, |
| 376 | * as well as a custom prefix/suffix around them |
| 377 | * |
| 378 | * Note that the prefix length is limited to {@link #getRenamePrefixLengthLimit} characters |
| 379 | * to ensure that there's enough space to display the byte data |
| 380 | * |
| 381 | * The range of name characters to be displayed cannot be empty |
| 382 | * |
| 383 | * @param prefix to be displayed before the byte data |
| 384 | * @param suffix to be displayed after the byte data |
| 385 | * @param nameFrom the start name character index to be displayed (inclusive) |
| 386 | * @param nameTo the end name character index to be displayed (exclusive) |
| 387 | * @return self for chaining |
| 388 | */ |
| 389 | @NonNull |
| 390 | public Builder setRenameFromName(@NonNull String prefix, @NonNull String suffix, |
| 391 | int nameFrom, int nameTo) { |
| 392 | checkRenameNotSet(); |
| 393 | checkRangeNotEmpty(nameFrom, nameTo); |
| 394 | mRenameNameFrom = nameFrom; |
| 395 | mRenameNameTo = nameTo; |
| 396 | mRenameBytesReverseOrder = false; |
| 397 | return setRename(prefix, suffix); |
| 398 | } |
| 399 | |
| 400 | private void checkRenameNotSet() { |
| 401 | checkState(mRenamePrefix == null, "Renaming rule can only be set once"); |
| 402 | } |
| 403 | |
| 404 | private void checkRangeNotEmpty(int bytesFrom, int bytesTo) { |
| 405 | checkArgument(bytesFrom < bytesTo, "Range must be non-empty"); |
| 406 | } |
| 407 | |
| 408 | @NonNull |
| 409 | private Builder setRename(@NonNull String prefix, @NonNull String suffix) { |
| 410 | checkNotUsed(); |
| 411 | checkArgument(TextUtils.length(prefix) <= getRenamePrefixLengthLimit(), |
| 412 | "Prefix is too long"); |
| 413 | mRenamePrefix = prefix; |
| 414 | mRenameSuffix = suffix; |
Eugene Susla | 36e866b | 2017-02-23 18:24:39 -0800 | [diff] [blame] | 415 | return this; |
| 416 | } |
| 417 | |
Eugene Susla | 6ed45d8 | 2017-01-22 13:52:51 -0800 | [diff] [blame] | 418 | /** @inheritDoc */ |
| 419 | @Override |
| 420 | @NonNull |
| 421 | public BluetoothLEDeviceFilter build() { |
| 422 | markUsed(); |
Eugene Susla | 36e866b | 2017-02-23 18:24:39 -0800 | [diff] [blame] | 423 | return new BluetoothLEDeviceFilter(mNamePattern, mScanFilter, |
| 424 | mRawDataFilter, mRawDataFilterMask, |
| 425 | mRenamePrefix, mRenameSuffix, |
Eugene Susla | 75fb821 | 2017-04-05 10:36:24 -0700 | [diff] [blame] | 426 | mRenameBytesFrom, mRenameBytesTo, |
| 427 | mRenameNameFrom, mRenameNameTo, |
| 428 | mRenameBytesReverseOrder); |
Eugene Susla | 6ed45d8 | 2017-01-22 13:52:51 -0800 | [diff] [blame] | 429 | } |
| 430 | } |
| 431 | } |