Home | History | Annotate | Download | only in nsd
      1 /*
      2  * Copyright (C) 2012 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.nsd;
     18 
     19 import android.net.nsd.DnsSdTxtRecord;
     20 import android.text.TextUtils;
     21 
     22 import java.util.ArrayList;
     23 import java.util.HashMap;
     24 import java.util.List;
     25 import java.util.Map;
     26 
     27 /**
     28  * A class for storing Bonjour service information that is advertised
     29  * over a Wi-Fi peer-to-peer setup.
     30  *
     31  * {@see android.net.wifi.p2p.WifiP2pManager#addLocalService}
     32  * {@see android.net.wifi.p2p.WifiP2pManager#removeLocalService}
     33  * {@see WifiP2pServiceInfo}
     34  * {@see WifiP2pUpnpServiceInfo}
     35  */
     36 public class WifiP2pDnsSdServiceInfo extends WifiP2pServiceInfo {
     37 
     38     /**
     39      * Bonjour version 1.
     40      * @hide
     41      */
     42     public static final int VERSION_1 = 0x01;
     43 
     44     /**
     45      * Pointer record.
     46      * @hide
     47      */
     48     public static final int DNS_TYPE_PTR = 12;
     49 
     50     /**
     51      * Text record.
     52      * @hide
     53      */
     54     public static final int DNS_TYPE_TXT = 16;
     55 
     56     /**
     57      * virtual memory packet.
     58      * see E.3 of the Wi-Fi Direct technical specification for the detail.<br>
     59      * Key: domain name Value: pointer address.<br>
     60      */
     61     private final static Map<String, String> sVmPacket;
     62 
     63     static {
     64         sVmPacket = new HashMap<String, String>();
     65         sVmPacket.put("_tcp.local.", "c00c");
     66         sVmPacket.put("local.", "c011");
     67         sVmPacket.put("_udp.local.", "c01c");
     68     }
     69 
     70     /**
     71      * This constructor is only used in newInstance().
     72      *
     73      * @param queryList
     74      */
     75     private WifiP2pDnsSdServiceInfo(List<String> queryList) {
     76         super(queryList);
     77     }
     78 
     79     /**
     80      * Create a Bonjour service information object.
     81      *
     82      * @param instanceName instance name.<br>
     83      *  e.g) "MyPrinter"
     84      * @param serviceType service type.<br>
     85      *  e.g) "_ipp._tcp"
     86      * @param txtMap TXT record with key/value pair in a map confirming to format defined at
     87      * http://files.dns-sd.org/draft-cheshire-dnsext-dns-sd.txt
     88      * @return Bonjour service information object
     89      */
     90     public static WifiP2pDnsSdServiceInfo newInstance(String instanceName,
     91             String serviceType, Map<String, String> txtMap) {
     92         if (TextUtils.isEmpty(instanceName) || TextUtils.isEmpty(serviceType)) {
     93             throw new IllegalArgumentException(
     94                     "instance name or service type cannot be empty");
     95         }
     96 
     97         DnsSdTxtRecord txtRecord = new DnsSdTxtRecord();
     98         if (txtMap != null) {
     99             for (String key : txtMap.keySet()) {
    100                 txtRecord.set(key, txtMap.get(key));
    101             }
    102         }
    103 
    104         ArrayList<String> queries = new ArrayList<String>();
    105         queries.add(createPtrServiceQuery(instanceName, serviceType));
    106         queries.add(createTxtServiceQuery(instanceName, serviceType, txtRecord));
    107 
    108         return new WifiP2pDnsSdServiceInfo(queries);
    109     }
    110 
    111     /**
    112      * Create wpa_supplicant service query for PTR record.
    113      *
    114      * @param instanceName instance name.<br>
    115      *  e.g) "MyPrinter"
    116      * @param serviceType service type.<br>
    117      *  e.g) "_ipp._tcp"
    118      * @return wpa_supplicant service query.
    119      */
    120     private static String createPtrServiceQuery(String instanceName,
    121             String serviceType) {
    122 
    123         StringBuffer sb = new StringBuffer();
    124         sb.append("bonjour ");
    125         sb.append(createRequest(serviceType + ".local.", DNS_TYPE_PTR, VERSION_1));
    126         sb.append(" ");
    127 
    128         byte[] data = instanceName.getBytes();
    129         sb.append(String.format("%02x", data.length));
    130         sb.append(WifiP2pServiceInfo.bin2HexStr(data));
    131         // This is the start point of this response.
    132         // Therefore, it indicates the request domain name.
    133         sb.append("c027");
    134         return sb.toString();
    135     }
    136 
    137     /**
    138      * Create wpa_supplicant service query for TXT record.
    139      *
    140      * @param instanceName instance name.<br>
    141      *  e.g) "MyPrinter"
    142      * @param serviceType service type.<br>
    143      *  e.g) "_ipp._tcp"
    144      * @param txtRecord TXT record.<br>
    145      * @return wpa_supplicant service query.
    146      */
    147     private static String createTxtServiceQuery(String instanceName,
    148             String serviceType,
    149             DnsSdTxtRecord txtRecord) {
    150 
    151 
    152         StringBuffer sb = new StringBuffer();
    153         sb.append("bonjour ");
    154 
    155         sb.append(createRequest((instanceName + "." + serviceType + ".local."),
    156                 DNS_TYPE_TXT, VERSION_1));
    157         sb.append(" ");
    158         byte[] rawData = txtRecord.getRawData();
    159         if (rawData.length == 0) {
    160             sb.append("00");
    161         } else {
    162             sb.append(bin2HexStr(rawData));
    163         }
    164         return sb.toString();
    165     }
    166 
    167     /**
    168      * Create bonjour service discovery request.
    169      *
    170      * @param dnsName dns name
    171      * @param dnsType dns type
    172      * @param version version number
    173      * @hide
    174      */
    175     static String createRequest(String dnsName, int dnsType, int version) {
    176         StringBuffer sb = new StringBuffer();
    177 
    178         /*
    179          * The request format is as follows.
    180          * ________________________________________________
    181          * |  Encoded and Compressed dns name (variable)  |
    182          * ________________________________________________
    183          * |   Type (2)           | Version (1) |
    184          */
    185         if (dnsType == WifiP2pDnsSdServiceInfo.DNS_TYPE_TXT) {
    186             dnsName = dnsName.toLowerCase();
    187         }
    188         sb.append(compressDnsName(dnsName));
    189         sb.append(String.format("%04x", dnsType));
    190         sb.append(String.format("%02x", version));
    191 
    192         return sb.toString();
    193     }
    194 
    195     /**
    196      * Compress DNS data.
    197      *
    198      * see E.3 of the Wi-Fi Direct technical specification for the detail.
    199      *
    200      * @param dnsName dns name
    201      * @return compressed dns name
    202      */
    203     private static String compressDnsName(String dnsName) {
    204         StringBuffer sb = new StringBuffer();
    205 
    206         // The domain name is replaced with a pointer to a prior
    207         // occurrence of the same name in virtual memory packet.
    208         while (true) {
    209             String data = sVmPacket.get(dnsName);
    210             if (data != null) {
    211                 sb.append(data);
    212                 break;
    213             }
    214             int i = dnsName.indexOf('.');
    215             if (i == -1) {
    216                 if (dnsName.length() > 0) {
    217                     sb.append(String.format("%02x", dnsName.length()));
    218                     sb.append(WifiP2pServiceInfo.bin2HexStr(dnsName.getBytes()));
    219                 }
    220                 // for a sequence of labels ending in a zero octet
    221                 sb.append("00");
    222                 break;
    223             }
    224 
    225             String name = dnsName.substring(0, i);
    226             dnsName = dnsName.substring(i + 1);
    227             sb.append(String.format("%02x", name.length()));
    228             sb.append(WifiP2pServiceInfo.bin2HexStr(name.getBytes()));
    229         }
    230         return sb.toString();
    231     }
    232 }
    233