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.matchesAddress; 21 import static android.companion.BluetoothDeviceFilterUtils.matchesName; 22 import static android.companion.BluetoothDeviceFilterUtils.matchesServiceUuids; 23 import static android.companion.BluetoothDeviceFilterUtils.patternFromString; 24 import static android.companion.BluetoothDeviceFilterUtils.patternToString; 25 26 import android.annotation.NonNull; 27 import android.annotation.Nullable; 28 import android.bluetooth.BluetoothDevice; 29 import android.compat.annotation.UnsupportedAppUsage; 30 import android.os.Build; 31 import android.os.Parcel; 32 import android.os.ParcelUuid; 33 import android.provider.OneTimeUseBuilder; 34 35 import com.android.internal.util.ArrayUtils; 36 import com.android.internal.util.CollectionUtils; 37 38 import java.util.ArrayList; 39 import java.util.List; 40 import java.util.Objects; 41 import java.util.regex.Pattern; 42 43 /** 44 * A filter for Bluetooth(non-LE) devices 45 */ 46 public final class BluetoothDeviceFilter implements DeviceFilter<BluetoothDevice> { 47 48 private final Pattern mNamePattern; 49 private final String mAddress; 50 private final List<ParcelUuid> mServiceUuids; 51 private final List<ParcelUuid> mServiceUuidMasks; 52 BluetoothDeviceFilter( Pattern namePattern, String address, List<ParcelUuid> serviceUuids, List<ParcelUuid> serviceUuidMasks)53 private BluetoothDeviceFilter( 54 Pattern namePattern, 55 String address, 56 List<ParcelUuid> serviceUuids, 57 List<ParcelUuid> serviceUuidMasks) { 58 mNamePattern = namePattern; 59 mAddress = address; 60 mServiceUuids = CollectionUtils.emptyIfNull(serviceUuids); 61 mServiceUuidMasks = CollectionUtils.emptyIfNull(serviceUuidMasks); 62 } 63 BluetoothDeviceFilter(Parcel in)64 private BluetoothDeviceFilter(Parcel in) { 65 this( 66 patternFromString(in.readString()), 67 in.readString(), 68 readUuids(in), 69 readUuids(in)); 70 } 71 readUuids(Parcel in)72 private static List<ParcelUuid> readUuids(Parcel in) { 73 return in.readParcelableList(new ArrayList<>(), ParcelUuid.class.getClassLoader(), android.os.ParcelUuid.class); 74 } 75 76 /** @hide */ 77 @Override matches(BluetoothDevice device)78 public boolean matches(BluetoothDevice device) { 79 return matchesAddress(mAddress, device) 80 && matchesServiceUuids(mServiceUuids, mServiceUuidMasks, device) 81 && matchesName(getNamePattern(), device); 82 } 83 84 /** @hide */ 85 @Override getDeviceDisplayName(BluetoothDevice device)86 public String getDeviceDisplayName(BluetoothDevice device) { 87 return getDeviceDisplayNameInternal(device); 88 } 89 90 /** @hide */ 91 @Override getMediumType()92 public int getMediumType() { 93 return DeviceFilter.MEDIUM_TYPE_BLUETOOTH; 94 } 95 96 /** @hide */ 97 @Nullable getNamePattern()98 public Pattern getNamePattern() { 99 return mNamePattern; 100 } 101 102 /** @hide */ 103 @Nullable 104 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) getAddress()105 public String getAddress() { 106 return mAddress; 107 } 108 109 /** @hide */ 110 @NonNull getServiceUuids()111 public List<ParcelUuid> getServiceUuids() { 112 return mServiceUuids; 113 } 114 115 /** @hide */ 116 @NonNull getServiceUuidMasks()117 public List<ParcelUuid> getServiceUuidMasks() { 118 return mServiceUuidMasks; 119 } 120 121 @Override writeToParcel(Parcel dest, int flags)122 public void writeToParcel(Parcel dest, int flags) { 123 dest.writeString(patternToString(getNamePattern())); 124 dest.writeString(mAddress); 125 dest.writeParcelableList(mServiceUuids, flags); 126 dest.writeParcelableList(mServiceUuidMasks, flags); 127 } 128 129 @Override equals(@ullable Object o)130 public boolean equals(@Nullable Object o) { 131 if (this == o) return true; 132 if (o == null || getClass() != o.getClass()) return false; 133 BluetoothDeviceFilter that = (BluetoothDeviceFilter) o; 134 return Objects.equals(mNamePattern, that.mNamePattern) && 135 Objects.equals(mAddress, that.mAddress) && 136 Objects.equals(mServiceUuids, that.mServiceUuids) && 137 Objects.equals(mServiceUuidMasks, that.mServiceUuidMasks); 138 } 139 140 @Override hashCode()141 public int hashCode() { 142 return Objects.hash(mNamePattern, mAddress, mServiceUuids, mServiceUuidMasks); 143 } 144 145 @Override toString()146 public String toString() { 147 return "BluetoothDeviceFilter{" 148 + "mNamePattern=" + mNamePattern 149 + ", mAddress='" + mAddress + '\'' 150 + ", mServiceUuids=" + mServiceUuids 151 + ", mServiceUuidMasks=" + mServiceUuidMasks 152 + '}'; 153 } 154 155 @Override describeContents()156 public int describeContents() { 157 return 0; 158 } 159 160 public static final @android.annotation.NonNull Creator<BluetoothDeviceFilter> CREATOR 161 = new Creator<BluetoothDeviceFilter>() { 162 @Override 163 public BluetoothDeviceFilter createFromParcel(Parcel in) { 164 return new BluetoothDeviceFilter(in); 165 } 166 167 @Override 168 public BluetoothDeviceFilter[] newArray(int size) { 169 return new BluetoothDeviceFilter[size]; 170 } 171 }; 172 173 /** 174 * A builder for {@link BluetoothDeviceFilter} 175 */ 176 public static final class Builder extends OneTimeUseBuilder<BluetoothDeviceFilter> { 177 private Pattern mNamePattern; 178 private String mAddress; 179 private ArrayList<ParcelUuid> mServiceUuid; 180 private ArrayList<ParcelUuid> mServiceUuidMask; 181 182 /** 183 * @param regex if set, only devices with {@link BluetoothDevice#getName name} matching the 184 * given regular expression will be shown 185 */ setNamePattern(@ullable Pattern regex)186 public Builder setNamePattern(@Nullable Pattern regex) { 187 checkNotUsed(); 188 mNamePattern = regex; 189 return this; 190 } 191 192 /** 193 * @param address if set, only devices with MAC address exactly matching the given one will 194 * pass the filter 195 */ 196 @NonNull setAddress(@ullable String address)197 public Builder setAddress(@Nullable String address) { 198 checkNotUsed(); 199 mAddress = address; 200 return this; 201 } 202 203 /** 204 * Add filtering by certain bits of {@link BluetoothDevice#getUuids()} 205 * 206 * A device with any uuid matching the given bits is considered passing 207 * 208 * @param serviceUuid the values for the bits to match 209 * @param serviceUuidMask if provided, only those bits would have to match. 210 */ 211 @NonNull addServiceUuid( @ullable ParcelUuid serviceUuid, @Nullable ParcelUuid serviceUuidMask)212 public Builder addServiceUuid( 213 @Nullable ParcelUuid serviceUuid, @Nullable ParcelUuid serviceUuidMask) { 214 checkNotUsed(); 215 mServiceUuid = ArrayUtils.add(mServiceUuid, serviceUuid); 216 mServiceUuidMask = ArrayUtils.add(mServiceUuidMask, serviceUuidMask); 217 return this; 218 } 219 220 /** @inheritDoc */ 221 @Override 222 @NonNull build()223 public BluetoothDeviceFilter build() { 224 markUsed(); 225 return new BluetoothDeviceFilter( 226 mNamePattern, mAddress, mServiceUuid, mServiceUuidMask); 227 } 228 } 229 } 230