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