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