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