OSDN Git Service

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