/*
* Copyright (C) 2012 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package android.net.wifi.p2p.nsd;
import android.net.wifi.p2p.WifiP2pDevice;
import java.io.ByteArrayInputStream;
import java.io.DataInputStream;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
/**
* A class for a response of bonjour service discovery.
*
* @hide
*/
public class WifiP2pDnsSdServiceResponse extends WifiP2pServiceResponse {
/**
* DNS query name.
* e.g)
* for PTR
* "_ipp._tcp.local."
* for TXT
* "MyPrinter._ipp._tcp.local."
*/
private String mDnsQueryName;
/**
* Service instance name.
* e.g) "MyPrinter"
* This field is only used when the dns type equals to
* {@link WifiP2pDnsSdServiceInfo#DNS_TYPE_PTR}.
*/
private String mInstanceName;
/**
* DNS Type.
* Should be {@link WifiP2pDnsSdServiceInfo#DNS_TYPE_PTR} or
* {@link WifiP2pDnsSdServiceInfo#DNS_TYPE_TXT}.
*/
private int mDnsType;
/**
* DnsSd version number.
* Should be {@link WifiP2pDnsSdServiceInfo#VERSION_1}.
*/
private int mVersion;
/**
* Txt record.
* This field is only used when the dns type equals to
* {@link WifiP2pDnsSdServiceInfo#DNS_TYPE_TXT}.
*/
private final HashMap<String, String> mTxtRecord = new HashMap<String, String>();
/**
* Virtual memory packet.
* see E.3 of the Wi-Fi Direct technical specification for the detail.<br>
* The spec can be obtained from wi-fi.org
* Key: pointer Value: domain name.<br>
*/
private final static Map<Integer, String> sVmpack;
static {
sVmpack = new HashMap<Integer, String>();
sVmpack.put(0x0c, "_tcp.local.");
sVmpack.put(0x11, "local.");
sVmpack.put(0x1c, "_udp.local.");
}
/**
* Returns query DNS name.
* @return DNS name.
*/
public String getDnsQueryName() {
return mDnsQueryName;
}
/**
* Return query DNS type.
* @return DNS type.
*/
public int getDnsType() {
return mDnsType;
}
/**
* Return bonjour version number.
* @return version number.
*/
public int getVersion() {
return mVersion;
}
/**
* Return instance name.
* @return
*/
public String getInstanceName() {
return mInstanceName;
}
/**
* Return TXT record data.
* @return TXT record data.
*/
public Map<String, String> getTxtRecord() {
return mTxtRecord;
}
@Override
public String toString() {
StringBuffer sbuf = new StringBuffer();
sbuf.append("serviceType:DnsSd(").append(mServiceType).append(")");
sbuf.append(" status:").append(Status.toString(mStatus));
sbuf.append(" srcAddr:").append(mDevice.deviceAddress);
sbuf.append(" version:").append(String.format("%02x", mVersion));
sbuf.append(" dnsName:").append(mDnsQueryName);
sbuf.append(" TxtRecord:");
for (String key : mTxtRecord.keySet()) {
sbuf.append(" key:").append(key).append(" value:").append(mTxtRecord.get(key));
}
if (mInstanceName != null) {
sbuf.append(" InsName:").append(mInstanceName);
}
return sbuf.toString();
}
/**
* This is only used in framework.
* @param status status code.
* @param dev source device.
* @param data RDATA.
* @hide
*/
protected WifiP2pDnsSdServiceResponse(int status,
int tranId, WifiP2pDevice dev, byte[] data) {
super(WifiP2pServiceInfo.SERVICE_TYPE_BONJOUR,
status, tranId, dev, data);
if (!parse()) {
throw new IllegalArgumentException("Malformed bonjour service response");
}
}
/**
* Parse DnsSd service discovery response.
*
* @return {@code true} if the operation succeeded
*/
private boolean parse() {
/*
* The data format from Wi-Fi Direct spec is as follows.
* ________________________________________________
* | encoded and compressed dns name (variable) |
* ________________________________________________
* | dnstype(2byte) | version(1byte) |
* ________________________________________________
* | RDATA (variable) |
*/
if (mData == null) {
// the empty is OK.
return true;
}
DataInputStream dis = new DataInputStream(new ByteArrayInputStream(mData));
mDnsQueryName = readDnsName(dis);
if (mDnsQueryName == null) {
return false;
}
try {
mDnsType = dis.readUnsignedShort();
mVersion = dis.readUnsignedByte();
} catch (IOException e) {
e.printStackTrace();
return false;
}
if (mDnsType == WifiP2pDnsSdServiceInfo.DNS_TYPE_PTR) {
String rData = readDnsName(dis);
if (rData == null) {
return false;
}
if (rData.length() <= mDnsQueryName.length()) {
return false;
}
mInstanceName = rData.substring(0,
rData.length() - mDnsQueryName.length() -1);
} else if (mDnsType == WifiP2pDnsSdServiceInfo.DNS_TYPE_TXT) {
return readTxtData(dis);
} else {
return false;
}
return true;
}
/**
* Read dns name.
*
* @param dis data input stream.
* @return dns name
*/
private String readDnsName(DataInputStream dis) {
StringBuffer sb = new StringBuffer();
// copy virtual memory packet.
HashMap<Integer, String> vmpack = new HashMap<Integer, String>(sVmpack);
if (mDnsQueryName != null) {
vmpack.put(0x27, mDnsQueryName);
}
try {
while (true) {
int i = dis.readUnsignedByte();
if (i == 0x00) {
return sb.toString();
} else if (i == 0xc0) {
// refer to pointer.
String ref = vmpack.get(dis.readUnsignedByte());
if (ref == null) {
//invalid.
return null;
}
sb.append(ref);
return sb.toString();
} else {
byte[] data = new byte[i];
dis.readFully(data);
sb.append(new String(data));
sb.append(".");
}
}
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
/**
* Read TXT record data.
*
* @param dis
* @return true if TXT data is valid
*/
private boolean readTxtData(DataInputStream dis) {
try {
while (dis.available() > 0) {
int len = dis.readUnsignedByte();
if (len == 0) {
break;
}
byte[] data = new byte[len];
dis.readFully(data);
String[] keyVal = new String(data).split("=");
if (keyVal.length != 2) {
return false;
}
mTxtRecord.put(keyVal[0], keyVal[1]);
}
return true;
} catch (IOException e) {
e.printStackTrace();
}
return false;
}
/**
* Creates DnsSd service response.
* This is only called from WifiP2pServiceResponse
*
* @param status status code.
* @param dev source device.
* @param data DnsSd response data.
* @return DnsSd service response data.
* @hide
*/
static WifiP2pDnsSdServiceResponse newInstance(int status,
int transId, WifiP2pDevice dev, byte[] data) {
if (status != WifiP2pServiceResponse.Status.SUCCESS) {
return new WifiP2pDnsSdServiceResponse(status,
transId, dev, null);
}
try {
return new WifiP2pDnsSdServiceResponse(status,
transId, dev, data);
} catch (IllegalArgumentException e) {
e.printStackTrace();
}
return null;
}
}
|