• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2011 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.net.wifi.p2p;
18 
19 import android.annotation.NonNull;
20 import android.annotation.Nullable;
21 import android.compat.annotation.UnsupportedAppUsage;
22 import android.os.Build;
23 import android.os.Parcel;
24 import android.os.Parcelable;
25 import android.util.Log;
26 
27 import com.android.modules.utils.build.SdkLevel;
28 
29 import java.util.Objects;
30 import java.util.regex.Matcher;
31 import java.util.regex.Pattern;
32 
33 /**
34  * A class representing a Wi-Fi p2p device
35  *
36  * Note that the operations are not thread safe
37  * {@see WifiP2pManager}
38  */
39 public class WifiP2pDevice implements Parcelable {
40 
41     private static final String TAG = "WifiP2pDevice";
42 
43     /**
44      * The device name is a user friendly string to identify a Wi-Fi p2p device
45      */
46     public String deviceName = "";
47 
48     /**
49      * The device MAC address uniquely identifies a Wi-Fi p2p device
50      */
51     public String deviceAddress = "";
52 
53     /**
54      * Primary device type identifies the type of device. For example, an application
55      * could filter the devices discovered to only display printers if the purpose is to
56      * enable a printing action from the user. See the Wi-Fi Direct technical specification
57      * for the full list of standard device types supported.
58      */
59     public String primaryDeviceType;
60 
61     /**
62      * Secondary device type is an optional attribute that can be provided by a device in
63      * addition to the primary device type.
64      */
65     public String secondaryDeviceType;
66 
67 
68     // These definitions match the ones in wpa_supplicant
69     /* WPS config methods supported */
70     private static final int WPS_CONFIG_DISPLAY         = 0x0008;
71     private static final int WPS_CONFIG_PUSHBUTTON      = 0x0080;
72     private static final int WPS_CONFIG_KEYPAD          = 0x0100;
73 
74     /* Device Capability bitmap */
75     private static final int DEVICE_CAPAB_SERVICE_DISCOVERY         = 1;
76     @SuppressWarnings("unused")
77     private static final int DEVICE_CAPAB_CLIENT_DISCOVERABILITY    = 1<<1;
78     @SuppressWarnings("unused")
79     private static final int DEVICE_CAPAB_CONCURRENT_OPER           = 1<<2;
80     @SuppressWarnings("unused")
81     private static final int DEVICE_CAPAB_INFRA_MANAGED             = 1<<3;
82     @SuppressWarnings("unused")
83     private static final int DEVICE_CAPAB_DEVICE_LIMIT              = 1<<4;
84     private static final int DEVICE_CAPAB_INVITATION_PROCEDURE      = 1<<5;
85 
86     /* Group Capability bitmap */
87     private static final int GROUP_CAPAB_GROUP_OWNER                = 1;
88     @SuppressWarnings("unused")
89     private static final int GROUP_CAPAB_PERSISTENT_GROUP           = 1<<1;
90     private static final int GROUP_CAPAB_GROUP_LIMIT                = 1<<2;
91     @SuppressWarnings("unused")
92     private static final int GROUP_CAPAB_INTRA_BSS_DIST             = 1<<3;
93     @SuppressWarnings("unused")
94     private static final int GROUP_CAPAB_CROSS_CONN                 = 1<<4;
95     @SuppressWarnings("unused")
96     private static final int GROUP_CAPAB_PERSISTENT_RECONN          = 1<<5;
97     @SuppressWarnings("unused")
98     private static final int GROUP_CAPAB_GROUP_FORMATION            = 1<<6;
99 
100     /**
101      * WPS config methods supported
102      * @hide
103      */
104     @UnsupportedAppUsage
105     public int wpsConfigMethodsSupported;
106 
107     /**
108      * Device capability
109      * @hide
110      */
111     @UnsupportedAppUsage
112     public int deviceCapability;
113 
114     /**
115      * Group capability
116      * @hide
117      */
118     @UnsupportedAppUsage
119     public int groupCapability;
120 
121     public static final int CONNECTED   = 0;
122     public static final int INVITED     = 1;
123     public static final int FAILED      = 2;
124     public static final int AVAILABLE   = 3;
125     public static final int UNAVAILABLE = 4;
126 
127     /** Device connection status */
128     public int status = UNAVAILABLE;
129 
130     /** @hide */
131     @UnsupportedAppUsage
132     public WifiP2pWfdInfo wfdInfo;
133 
134     /** Detailed device string pattern with WFD info
135      * Example:
136      *  P2P-DEVICE-FOUND 00:18:6b:de:a3:6e p2p_dev_addr=00:18:6b:de:a3:6e
137      *  pri_dev_type=1-0050F204-1 name='DWD-300-DEA36E' config_methods=0x188
138      *  dev_capab=0x21 group_capab=0x9
139      */
140     private static final Pattern detailedDevicePattern = Pattern.compile(
141             "((?:[0-9a-f]{2}:){5}[0-9a-f]{2}) "
142             + "(\\d+ )?"
143             + "p2p_dev_addr=((?:[0-9a-f]{2}:){5}[0-9a-f]{2}) "
144             + "pri_dev_type=(\\d+-[0-9a-fA-F]+-\\d+) "
145             + "name='(.*)' "
146             + "config_methods=(0x[0-9a-fA-F]+) "
147             + "dev_capab=(0x[0-9a-fA-F]+) "
148             + "group_capab=(0x[0-9a-fA-F]+)"
149             + "( wfd_dev_info=0x([0-9a-fA-F]{12}))?"
150             + "( wfd_r2_dev_info=0x([0-9a-fA-F]{4}))?"
151     );
152 
153     /** 2 token device address pattern
154      * Example:
155      *  P2P-DEVICE-LOST p2p_dev_addr=fa:7b:7a:42:02:13
156      *  AP-STA-DISCONNECTED 42:fc:89:a8:96:09
157      */
158     private static final Pattern twoTokenPattern = Pattern.compile(
159         "(p2p_dev_addr=)?((?:[0-9a-f]{2}:){5}[0-9a-f]{2})"
160     );
161 
162     /** 3 token device address pattern
163      * Example:
164      *  AP-STA-CONNECTED 42:fc:89:a8:96:09 p2p_dev_addr=fa:7b:7a:42:02:13
165      *  AP-STA-DISCONNECTED 42:fc:89:a8:96:09 p2p_dev_addr=fa:7b:7a:42:02:13
166      */
167     private static final Pattern threeTokenPattern = Pattern.compile(
168         "(?:[0-9a-f]{2}:){5}[0-9a-f]{2} p2p_dev_addr=((?:[0-9a-f]{2}:){5}[0-9a-f]{2})"
169     );
170 
171 
WifiP2pDevice()172     public WifiP2pDevice() {
173     }
174 
175     /**
176      * @param string formats supported include
177      *  P2P-DEVICE-FOUND fa:7b:7a:42:02:13 p2p_dev_addr=fa:7b:7a:42:02:13
178      *  pri_dev_type=1-0050F204-1 name='p2p-TEST1' config_methods=0x188 dev_capab=0x27
179      *  group_capab=0x0 wfd_dev_info=000006015d022a0032
180      *
181      *  P2P-DEVICE-LOST p2p_dev_addr=fa:7b:7a:42:02:13
182      *
183      *  AP-STA-CONNECTED 42:fc:89:a8:96:09 [p2p_dev_addr=02:90:4c:a0:92:54]
184      *
185      *  AP-STA-DISCONNECTED 42:fc:89:a8:96:09 [p2p_dev_addr=02:90:4c:a0:92:54]
186      *
187      *  fa:7b:7a:42:02:13
188      *
189      *  Note: The events formats can be looked up in the wpa_supplicant code
190      * @hide
191      */
192     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
WifiP2pDevice(String string)193     public WifiP2pDevice(String string) throws IllegalArgumentException {
194         String[] tokens = string.split("[ \n]");
195         Matcher match;
196 
197         if (tokens.length < 1) {
198             throw new IllegalArgumentException("Malformed supplicant event");
199         }
200 
201         switch (tokens.length) {
202             case 1:
203                 /* Just a device address */
204                 deviceAddress = string;
205                 return;
206             case 2:
207                 match = twoTokenPattern.matcher(string);
208                 if (!match.find()) {
209                     throw new IllegalArgumentException("Malformed supplicant event");
210                 }
211                 deviceAddress = match.group(2);
212                 return;
213             case 3:
214                 match = threeTokenPattern.matcher(string);
215                 if (!match.find()) {
216                     throw new IllegalArgumentException("Malformed supplicant event");
217                 }
218                 deviceAddress = match.group(1);
219                 return;
220             default:
221                 match = detailedDevicePattern.matcher(string);
222                 if (!match.find()) {
223                     throw new IllegalArgumentException("Malformed supplicant event");
224                 }
225 
226                 deviceAddress = match.group(3);
227                 primaryDeviceType = match.group(4);
228                 deviceName = match.group(5);
229                 wpsConfigMethodsSupported = parseHex(match.group(6));
230                 deviceCapability = parseHex(match.group(7));
231                 groupCapability = parseHex(match.group(8));
232                 if (match.group(9) != null) {
233                     String str = match.group(10);
234                     if (null == str) break;
235                     wfdInfo = new WifiP2pWfdInfo(parseHex(str.substring(0,4)),
236                             parseHex(str.substring(4,8)),
237                             parseHex(str.substring(8,12)));
238                     if (match.group(11) != null && SdkLevel.isAtLeastS()) {
239                         String r2str = match.group(12);
240                         if (null == r2str) break;
241                         wfdInfo.setR2DeviceType(parseHex(r2str.substring(0, 4)));
242                     }
243                 }
244                 break;
245         }
246 
247         if (tokens[0].startsWith("P2P-DEVICE-FOUND")) {
248             status = AVAILABLE;
249         }
250     }
251 
252     /** The Wifi Display information for this device, or null if unavailable. */
253     @Nullable
getWfdInfo()254     public WifiP2pWfdInfo getWfdInfo() {
255         return wfdInfo;
256     }
257 
258     /** Returns true if WPS push button configuration is supported */
wpsPbcSupported()259     public boolean wpsPbcSupported() {
260         return (wpsConfigMethodsSupported & WPS_CONFIG_PUSHBUTTON) != 0;
261     }
262 
263     /** Returns true if WPS keypad configuration is supported */
wpsKeypadSupported()264     public boolean wpsKeypadSupported() {
265         return (wpsConfigMethodsSupported & WPS_CONFIG_KEYPAD) != 0;
266     }
267 
268     /** Returns true if WPS display configuration is supported */
wpsDisplaySupported()269     public boolean wpsDisplaySupported() {
270         return (wpsConfigMethodsSupported & WPS_CONFIG_DISPLAY) != 0;
271     }
272 
273     /** Returns true if the device is capable of service discovery */
isServiceDiscoveryCapable()274     public boolean isServiceDiscoveryCapable() {
275         return (deviceCapability & DEVICE_CAPAB_SERVICE_DISCOVERY) != 0;
276     }
277 
278     /** Returns true if the device is capable of invitation {@hide}*/
isInvitationCapable()279     public boolean isInvitationCapable() {
280         return (deviceCapability & DEVICE_CAPAB_INVITATION_PROCEDURE) != 0;
281     }
282 
283     /** Returns true if the device reaches the limit. {@hide}*/
isDeviceLimit()284     public boolean isDeviceLimit() {
285         return (deviceCapability & DEVICE_CAPAB_DEVICE_LIMIT) != 0;
286     }
287 
288     /** Returns true if the device is a group owner */
isGroupOwner()289     public boolean isGroupOwner() {
290         return (groupCapability & GROUP_CAPAB_GROUP_OWNER) != 0;
291     }
292 
293     /** Returns true if the group reaches the limit. {@hide}*/
isGroupLimit()294     public boolean isGroupLimit() {
295         return (groupCapability & GROUP_CAPAB_GROUP_LIMIT) != 0;
296     }
297 
298     /**
299      * Update this device's details using another {@link WifiP2pDevice} instance.
300      * This will throw an exception if the device address does not match.
301      *
302      * @param device another instance of {@link WifiP2pDevice} used to update this instance.
303      * @throws IllegalArgumentException if the device is null or the device address does not match
304      */
update(@onNull WifiP2pDevice device)305     public void update(@NonNull WifiP2pDevice device) {
306         updateSupplicantDetails(device);
307         status = device.status;
308     }
309 
310     /** Updates details obtained from supplicant @hide */
updateSupplicantDetails(WifiP2pDevice device)311     public void updateSupplicantDetails(WifiP2pDevice device) {
312         if (device == null) {
313             throw new IllegalArgumentException("device is null");
314         }
315         if (device.deviceAddress == null) {
316             throw new IllegalArgumentException("deviceAddress is null");
317         }
318         if (!deviceAddress.equals(device.deviceAddress)) {
319             throw new IllegalArgumentException("deviceAddress does not match");
320         }
321         deviceName = device.deviceName;
322         primaryDeviceType = device.primaryDeviceType;
323         secondaryDeviceType = device.secondaryDeviceType;
324         wpsConfigMethodsSupported = device.wpsConfigMethodsSupported;
325         deviceCapability = device.deviceCapability;
326         groupCapability = device.groupCapability;
327         wfdInfo = device.wfdInfo;
328     }
329 
330     @Override
equals(Object obj)331     public boolean equals(Object obj) {
332         if (this == obj) return true;
333         if (!(obj instanceof WifiP2pDevice)) return false;
334 
335         WifiP2pDevice other = (WifiP2pDevice) obj;
336         if (other == null || other.deviceAddress == null) {
337             return (deviceAddress == null);
338         }
339         return other.deviceAddress.equals(deviceAddress);
340     }
341 
342     @Override
hashCode()343     public int hashCode() {
344         return Objects.hashCode(deviceAddress);
345     }
346 
347     @Override
toString()348     public String toString() {
349         StringBuffer sbuf = new StringBuffer();
350         sbuf.append("Device: ").append(deviceName);
351         sbuf.append("\n deviceAddress: ").append(deviceAddress);
352         sbuf.append("\n primary type: ").append(primaryDeviceType);
353         sbuf.append("\n secondary type: ").append(secondaryDeviceType);
354         sbuf.append("\n wps: ").append(wpsConfigMethodsSupported);
355         sbuf.append("\n grpcapab: ").append(groupCapability);
356         sbuf.append("\n devcapab: ").append(deviceCapability);
357         sbuf.append("\n status: ").append(status);
358         sbuf.append("\n wfdInfo: ").append(wfdInfo);
359         return sbuf.toString();
360     }
361 
362     /** Implement the Parcelable interface */
363     @Override
describeContents()364     public int describeContents() {
365         return 0;
366     }
367 
368     /** copy constructor */
WifiP2pDevice(WifiP2pDevice source)369     public WifiP2pDevice(WifiP2pDevice source) {
370         if (source != null) {
371             deviceName = source.deviceName;
372             deviceAddress = source.deviceAddress;
373             primaryDeviceType = source.primaryDeviceType;
374             secondaryDeviceType = source.secondaryDeviceType;
375             wpsConfigMethodsSupported = source.wpsConfigMethodsSupported;
376             deviceCapability = source.deviceCapability;
377             groupCapability = source.groupCapability;
378             status = source.status;
379             if (source.wfdInfo != null) {
380                 wfdInfo = new WifiP2pWfdInfo(source.wfdInfo);
381             }
382         }
383     }
384 
385     /** Implement the Parcelable interface */
386     @Override
writeToParcel(Parcel dest, int flags)387     public void writeToParcel(Parcel dest, int flags) {
388         dest.writeString(deviceName);
389         dest.writeString(deviceAddress);
390         dest.writeString(primaryDeviceType);
391         dest.writeString(secondaryDeviceType);
392         dest.writeInt(wpsConfigMethodsSupported);
393         dest.writeInt(deviceCapability);
394         dest.writeInt(groupCapability);
395         dest.writeInt(status);
396         if (wfdInfo != null) {
397             dest.writeInt(1);
398             wfdInfo.writeToParcel(dest, flags);
399         } else {
400             dest.writeInt(0);
401         }
402     }
403 
404     /** Implement the Parcelable interface */
405     public static final @android.annotation.NonNull Creator<WifiP2pDevice> CREATOR =
406         new Creator<WifiP2pDevice>() {
407             @Override
408             public WifiP2pDevice createFromParcel(Parcel in) {
409                 WifiP2pDevice device = new WifiP2pDevice();
410                 device.deviceName = in.readString();
411                 device.deviceAddress = in.readString();
412                 device.primaryDeviceType = in.readString();
413                 device.secondaryDeviceType = in.readString();
414                 device.wpsConfigMethodsSupported = in.readInt();
415                 device.deviceCapability = in.readInt();
416                 device.groupCapability = in.readInt();
417                 device.status = in.readInt();
418                 if (in.readInt() == 1) {
419                     device.wfdInfo = WifiP2pWfdInfo.CREATOR.createFromParcel(in);
420                 }
421                 return device;
422             }
423 
424             @Override
425             public WifiP2pDevice[] newArray(int size) {
426                 return new WifiP2pDevice[size];
427             }
428         };
429 
430     //supported formats: 0x1abc, 0X1abc, 1abc
parseHex(String hexString)431     private int parseHex(String hexString) {
432         int num = 0;
433         if (hexString.startsWith("0x") || hexString.startsWith("0X")) {
434             hexString = hexString.substring(2);
435         }
436 
437         try {
438             num = Integer.parseInt(hexString, 16);
439         } catch(NumberFormatException e) {
440             Log.e(TAG, "Failed to parse hex string " + hexString);
441         }
442         return num;
443     }
444 }
445