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 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 */ 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 */ 234 public boolean wpsPbcSupported() { 235 return (wpsConfigMethodsSupported & WPS_CONFIG_PUSHBUTTON) != 0; 236 } 237 238 /** Returns true if WPS keypad configuration is supported */ 239 public boolean wpsKeypadSupported() { 240 return (wpsConfigMethodsSupported & WPS_CONFIG_KEYPAD) != 0; 241 } 242 243 /** Returns true if WPS display configuration is supported */ 244 public boolean wpsDisplaySupported() { 245 return (wpsConfigMethodsSupported & WPS_CONFIG_DISPLAY) != 0; 246 } 247 248 /** Returns true if the device is capable of service discovery */ 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}*/ 254 public boolean isInvitationCapable() { 255 return (deviceCapability & DEVICE_CAPAB_INVITATION_PROCEDURE) != 0; 256 } 257 258 /** Returns true if the device reaches the limit. {@hide}*/ 259 public boolean isDeviceLimit() { 260 return (deviceCapability & DEVICE_CAPAB_DEVICE_LIMIT) != 0; 261 } 262 263 /** Returns true if the device is a group owner */ 264 public boolean isGroupOwner() { 265 return (groupCapability & GROUP_CAPAB_GROUP_OWNER) != 0; 266 } 267 268 /** Returns true if the group reaches the limit. {@hide}*/ 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 */ 280 public void update(WifiP2pDevice device) { 281 updateSupplicantDetails(device); 282 status = device.status; 283 } 284 285 /** Updates details obtained from supplicant @hide */ 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 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 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 334 public int describeContents() { 335 return 0; 336 } 337 338 /** copy constructor */ 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 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 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