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