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 21 import android.annotation.NonNull; 22 import android.annotation.Nullable; 23 import android.bluetooth.BluetoothDevice; 24 import android.bluetooth.le.ScanFilter; 25 import android.net.MacAddress; 26 import android.net.wifi.ScanResult; 27 import android.os.Parcel; 28 import android.os.Parcelable; 29 30 import com.android.internal.util.Parcelling; 31 32 import java.util.Objects; 33 import java.util.regex.Pattern; 34 35 /** 36 * A filter for Wifi devices 37 * 38 * @see ScanFilter 39 */ 40 public final class WifiDeviceFilter implements DeviceFilter<ScanResult> { 41 42 /** 43 * If set, only devices with {@link BluetoothDevice#getName name} matching the given regular 44 * expression will be shown 45 */ 46 @Nullable 47 private final Pattern mNamePattern; 48 49 /** 50 * If set, only devices with BSSID matching the given one will be shown 51 */ 52 @Nullable 53 private final MacAddress mBssid; 54 55 /** 56 * If set, only bits at positions set in this mask, will be compared to the given 57 * {@link Builder#setBssid BSSID} filter. 58 */ 59 @NonNull 60 private final MacAddress mBssidMask; 61 62 /** @hide */ 63 @Override matches(ScanResult device)64 public boolean matches(ScanResult device) { 65 return BluetoothDeviceFilterUtils.matchesName(getNamePattern(), device) 66 && (mBssid == null 67 || MacAddress.fromString(device.BSSID).matches(mBssid, mBssidMask)); 68 } 69 70 /** @hide */ 71 @Override getDeviceDisplayName(ScanResult device)72 public String getDeviceDisplayName(ScanResult device) { 73 return getDeviceDisplayNameInternal(device); 74 } 75 76 /** @hide */ 77 @Override getMediumType()78 public int getMediumType() { 79 return MEDIUM_TYPE_WIFI; 80 } 81 WifiDeviceFilter( @ullable Pattern namePattern, @Nullable MacAddress bssid, @NonNull MacAddress bssidMask)82 /* package-private */ WifiDeviceFilter( 83 @Nullable Pattern namePattern, 84 @Nullable MacAddress bssid, 85 @NonNull MacAddress bssidMask) { 86 this.mNamePattern = namePattern; 87 this.mBssid = bssid; 88 this.mBssidMask = bssidMask; 89 com.android.internal.util.AnnotationValidations.validate( 90 NonNull.class, null, mBssidMask); 91 } 92 93 /** 94 * If set, only devices with {@link BluetoothDevice#getName name} matching the given regular 95 * expression will be shown 96 * 97 * @hide 98 */ 99 @Nullable getNamePattern()100 public Pattern getNamePattern() { 101 return mNamePattern; 102 } 103 104 /** 105 * If set, only devices with BSSID matching the given one will be shown 106 * 107 * @hide 108 */ 109 @Nullable getBssid()110 public MacAddress getBssid() { 111 return mBssid; 112 } 113 114 /** 115 * If set, only bits at positions set in this mask, will be compared to the given 116 * {@link Builder#setBssid BSSID} filter. 117 * 118 * @hide 119 */ 120 @NonNull getBssidMask()121 public MacAddress getBssidMask() { 122 return mBssidMask; 123 } 124 125 @Override equals(@ullable Object o)126 public boolean equals(@Nullable Object o) { 127 if (this == o) return true; 128 if (o == null || getClass() != o.getClass()) return false; 129 WifiDeviceFilter that = (WifiDeviceFilter) o; 130 return Objects.equals(mNamePattern, that.mNamePattern) 131 && Objects.equals(mBssid, that.mBssid) 132 && Objects.equals(mBssidMask, that.mBssidMask); 133 } 134 135 @Override hashCode()136 public int hashCode() { 137 int _hash = 1; 138 _hash = 31 * _hash + Objects.hashCode(mNamePattern); 139 _hash = 31 * _hash + Objects.hashCode(mBssid); 140 _hash = 31 * _hash + Objects.hashCode(mBssidMask); 141 return _hash; 142 } 143 144 static Parcelling<Pattern> sParcellingForNamePattern = 145 Parcelling.Cache.get(Parcelling.BuiltIn.ForPattern.class); 146 static { 147 if (sParcellingForNamePattern == null) { 148 sParcellingForNamePattern = Parcelling.Cache.put( 149 new Parcelling.BuiltIn.ForPattern()); 150 } 151 } 152 153 @Override writeToParcel(@onNull Parcel dest, int flags)154 public void writeToParcel(@NonNull Parcel dest, int flags) { 155 byte flg = 0; 156 if (mNamePattern != null) flg |= 0x1; 157 if (mBssid != null) flg |= 0x2; 158 dest.writeByte(flg); 159 sParcellingForNamePattern.parcel(mNamePattern, dest, flags); 160 if (mBssid != null) dest.writeTypedObject(mBssid, flags); 161 dest.writeTypedObject(mBssidMask, flags); 162 } 163 164 @Override describeContents()165 public int describeContents() { 166 return 0; 167 } 168 169 /** @hide */ WifiDeviceFilter(@onNull Parcel in)170 /* package-private */ WifiDeviceFilter(@NonNull Parcel in) { 171 byte flg = in.readByte(); 172 Pattern namePattern = sParcellingForNamePattern.unparcel(in); 173 MacAddress bssid = (flg & 0x2) == 0 ? null : in.readTypedObject(MacAddress.CREATOR); 174 MacAddress bssidMask = in.readTypedObject(MacAddress.CREATOR); 175 176 this.mNamePattern = namePattern; 177 this.mBssid = bssid; 178 this.mBssidMask = bssidMask; 179 com.android.internal.util.AnnotationValidations.validate( 180 NonNull.class, null, mBssidMask); 181 } 182 183 @NonNull 184 public static final Parcelable.Creator<WifiDeviceFilter> CREATOR = 185 new Parcelable.Creator<WifiDeviceFilter>() { 186 @Override 187 public WifiDeviceFilter[] newArray(int size) { 188 return new WifiDeviceFilter[size]; 189 } 190 191 @Override 192 public WifiDeviceFilter createFromParcel(@NonNull Parcel in) { 193 return new WifiDeviceFilter(in); 194 } 195 }; 196 197 /** 198 * A builder for {@link WifiDeviceFilter} 199 */ 200 @SuppressWarnings("WeakerAccess") 201 public static final class Builder { 202 203 @Nullable private Pattern mNamePattern; 204 @Nullable private MacAddress mBssid; 205 @NonNull private MacAddress mBssidMask; 206 207 private long mBuilderFieldsSet = 0L; 208 Builder()209 public Builder() { 210 } 211 212 /** 213 * If set, only devices with {@link BluetoothDevice#getName name} matching the given regular 214 * expression will be shown 215 */ 216 @NonNull setNamePattern(@ullable Pattern value)217 public Builder setNamePattern(@Nullable Pattern value) { 218 checkNotUsed(); 219 mBuilderFieldsSet |= 0x1; 220 mNamePattern = value; 221 return this; 222 } 223 224 /** 225 * If set, only devices with BSSID matching the given one will be shown 226 */ 227 @NonNull setBssid(@onNull MacAddress value)228 public Builder setBssid(@NonNull MacAddress value) { 229 checkNotUsed(); 230 mBuilderFieldsSet |= 0x2; 231 mBssid = value; 232 return this; 233 } 234 235 /** 236 * If set, only bits at positions set in this mask, will be compared to the given 237 * {@link Builder#setBssid BSSID} filter. 238 */ 239 @NonNull setBssidMask(@onNull MacAddress value)240 public Builder setBssidMask(@NonNull MacAddress value) { 241 checkNotUsed(); 242 mBuilderFieldsSet |= 0x4; 243 mBssidMask = value; 244 return this; 245 } 246 247 /** Builds the instance. This builder should not be touched after calling this! */ 248 @NonNull build()249 public WifiDeviceFilter build() { 250 checkNotUsed(); 251 mBuilderFieldsSet |= 0x8; // Mark builder used 252 253 if ((mBuilderFieldsSet & 0x1) == 0) { 254 mNamePattern = null; 255 } 256 if ((mBuilderFieldsSet & 0x2) == 0) { 257 mBssid = null; 258 } 259 if ((mBuilderFieldsSet & 0x4) == 0) { 260 mBssidMask = MacAddress.BROADCAST_ADDRESS; 261 } 262 return new WifiDeviceFilter( 263 mNamePattern, 264 mBssid, 265 mBssidMask); 266 } 267 checkNotUsed()268 private void checkNotUsed() { 269 if ((mBuilderFieldsSet & 0x8) != 0) { 270 throw new IllegalStateException( 271 "This Builder should not be reused. Use a new Builder instance instead"); 272 } 273 } 274 } 275 } 276