Home | History | Annotate | Download | only in p2p
      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